浅谈数据库事务

概览

最近在做数据库相关的中间件,为了更深入的了解数据库相关知识,整理了一下一些资料,尝试讲述一下关于数据库的相关知识。由于数据库的知识博大精深,因此分了几个章节来讲述。

首先我们先来了解一个概念,数据库跟我们文件系统最大的区别,就是其事务特性。它是我们在访问并更新数据库各个数据项的一个程序执行单元。保证了我们数据库从一个一致的状态转换为另外一个的一致状态。

在文件系统中,我们没办法保证在操作两个或多个文件时,能同时成功(或者同时失败)。这个道理很容易理解,当我们操作完一个文件时,突然发生宕机或各种情况,导致后续操作终止了,当该文件系统重启或恢复时,已经操作的文件也无法回滚了,未操作的文件,有可能程序也不知道该从哪个时间点开始切入继续执行了。

因此数据库事务的意义就诞生了。理论上,数据库事务在设计时,必须满足了ACID的特性(虽然是理论上,但是厂商出于各种目的,并没严格遵循这个标准)。如Oracle的事务隔离级别是READ COMMITED,就不满足I的要求。

事务的ACID特性

  • 原子性
  • 一致性
  • 隔离性
  • 持久性

原子性(Atomicity)

保证在一个事务中的操作是原子操作。整个数据库事务是不可分割的工作单位。只有事务所有操作执行成功才算成功。

e.g 我们银行转账,A账户往B账户转200元。在A账户扣除200元的同时,B账户也需要增加200元,这两个操作必须同时成功或同时失败。否则只有一个成功,那么要不就A少了200元,要不B多了200元。凭空就产生了200元漏洞,这明显不可接受。

一致性(Consistency)

保证在一个事务执行前后,数据保证一致。简单说,就是数据库从一种状态转变为另外一种状态。如数据库完整性约束在事务前后没被破坏,数据库的唯一性约束在事务前后没被破坏等。

e.g 保证数据库完整性约束,a+b=10,一个事务改变了a,b也随之改变。又例如,一个有唯一约束的表,事务前后都不违背其唯一约束的特性。

隔离性(Isolation)

也称为并发控制、锁等。使用悲观或乐观锁机制实现(后续关于数据库文章会详细讲述)。确保并发执行的事务能按顺序一个一个的执行,一个未完成的事务并不会影响另一个事务(即每个读写事务都相互分离,互相不可见,通常可以用锁来实现),当前数据库提供了一种粒度锁来锁住一个实体对象的子集(可以理解为锁住了表中的某一行,甚至更深入等等)以此来提高并发度。

e.g mysql innodb中默认使用READ REPEATABLE,符合隔离性的要求。但是想oracle默认使用READ COMMITED,提高了效率,就没满足隔离性的要求(一个事务影响着另外一个事务)。但是有可能引发另外的问题,这个后面再来讨论。

持久性(Durability)

一个成功提交(commit)的事务,将永久改变其系统状态。即使由于系统崩溃或断电,事务中的数据改变未写入磁盘,那么系统恢复后也会将此数据恢复。但是需要明白这个持久性是保证了数据库的可靠性,并不是保证数据库的可用性,如某个数据库RAID卡损坏、各种自然灾害等等,这些需要的是数据库的可用性来保证,现在针对于数据库的可用性,已经有很多方案了,在这里就先不做阐述了。

这里可能有人会好奇,既然我都提交(commit)了,那么我的数据理所当然的保存在存储系统中了,这还属于一个特性?

实际上,在Mysql中,数据库为了高效,我们提交事务时,并不会立刻写入数据文件中(如果每个提交都写入数据文件中,那我们的数据库性能也太差了),中间通过了类似各种buffer,redo,undo等等的日志文件(这些后续的章节都会聊到),因此这个持久性,这么看来就很有必要了。

了解完了我们的数据库事务特性以后,我们再来看看我们现在事务运用场景的几个类别。

事务的类型

  • 扁平事务
  • 带保存点的扁平事务
  • 链式事务
  • 嵌套事务
  • 分布式事务

扁平事务

实际上我们最平常使用的就是扁平事务。通常就以一个begin开始事务,以一个commit作为结束事务(甚至以一个rollback来回滚事务)。

现在在Java甚至很多语言的实现中,甚至已经不用你去begin或commit的操作了,DAL框架基本帮你做完了这个事。如果你的Java中使用spring这些DAL框架,可以回忆一下是不是在程序中配置过TransactionManager,然后又将这个transactionManager运用到各种aop上,实际上运用在aop时,它以方法为切面,就已经帮你开启和结束了事务。这种傻瓜式的编程,对程序开发者是极为方便的,但也是一种慢性毒药,慢慢让开发者变成傻瓜,云里雾里的操作完了数据库,也不知道自己用了事务的操作。

甚至于我们在使用jdbc(Java操作数据库的基本类库)时,由于数据库的连接(Connection)是自动提交事务的,因此我们根本不需要去启停事务,直接操作就是了。完了底层帮你将每个sql语句作为一个一个的事务提交了,还是屏蔽掉了事务的细节,让你一傻再傻。

存在的问题:
扁平事务是我们最常用的例子,但是既然存在别的种类的事务,那就代表它不能满足我们所有需求。

我们可以想象一下,当我们在xx旅行网上买一个北京到悉尼的机票时,发现没有直达的机票,那么我们只能选择中转方案,如先买北京到香港的机票,再买香港到悉尼的。但是买完了北京到香港的,我们发现香港到悉尼的机票当天无票了,需要下一天,那这时候,我们不可能把你北京到香港的机票也回滚重新再来执行一次吧,这也太不智能了。因此我们由此引出第二个类别:带保存点的扁平事务。

带保存点扁平事务

带保存点的扁平事务,实际上就解决了我们上述的问题了。当我们买完从北京到香港的机票的时候,记录一下保存点(savepoint),当我想做回滚操作时,我就回退到我想要的保存点即可。

实际上扁平事务也属于带保存点的扁平事务,只不过他只有一个保存点,在其开始事务的时候,因此我们回滚操作,也只能回滚到最初的操作。

保存点操作在数据库中使用SAVE WORK函数来操作。拿网上都有的一张图来看一下这个带保存点的扁平事务:


带保存点事务.jpg

保存点是单调递增的(即使被回滚),可以从途中看到,即使3,4的保存点被回滚,下一个保存点也是从5开始计数。

链式事务

链式事务是带保存点扁平事务的一个变种,我们先来看一下下面的图:


链式事务.jpg

在提交T1事务的同时,开启T2事务,这两个操作是一个原子操作。且提交完T1,则释放T1的数据对象。因此回滚操作只能在当前事务中进行,如T2C回滚到T2A这种操作。

嵌套事务

嵌套数据库事务.jpg

关于嵌套数据库事务,我们对着图来说。首先有几个比较显著的特点:

  1. 所有与数据库相关的操作均在叶子节点(可以多层嵌套)的事务中。且叶子节点必然为扁平事务。
  2. 某个事务回滚,其所有子事务均回滚。
  3. 子事务提交,操作不立刻生效,即不满足ACID中的D特性,只有父亲事务提交,才是真正的提交。即最终所有的提交都在顶层事务。

以上是嵌套事务的一些特点,但是并不是所有的数据库都实现了嵌套事务,像Mysql的InnoDB就没有嵌套事务。事实上,我们发现,嵌套事务大多也可以用带保存点的事务来实现,我们先来看一下下面这种图,就是用带保存点的扁平事务来实现嵌套事务的功能


带保存点的扁平事务实现嵌套事务.jpg

从图中我们可以看出来,Tk1,Tk11,Tk12,Tk121,Tk2,Tk3,Tk31都是保存点。我们可以实现回滚到任意一个保存点。这样的功能甚至比嵌套事务更灵活(只能回滚自己的事务,或者由父亲来回滚所有子事务)。

如此灵活带来了另外的一个问题,其事务中所有的锁都是共享的,因为这扁平事务中,修改的任何数据,实际都在这个事务中,因此对后面各个保存点(虚拟出来的子事务)都是可见的。而嵌套事务,实际上可以由父亲来决定是否要将其持有的锁(其做的修改)传递给子事务中。

因此带保存点的扁平事务虽然可以模仿嵌套事务,但是是带着缺陷的模仿。

分布式事务

分布式事务,实际上就是在一个分布式环境下的扁平事务,通过例子瞬间就可以理解

e.g 我们去银行做转账(譬如招商银行转账到中国银行),通过银行终端点击转账时,实际上会有2个步骤:

  1. 从招商银行的数据库中划走这笔钱
  2. 从中国银行的数据库中加入这笔钱

这两个步骤必须同时成功,或同时失败,否则必然是灾难的。这就是我们所说的分布式事务了。

结束语

上面简单聊了一下关于数据库事务的特性和类型。里面很多处讲到了关于锁,还有关于ACID的特性,在后面会有更多的篇幅来讲述相关的内容,请继续关注后续文章。

微信公众号:酱君挺怎样
知乎:酱君挺怎样

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343

推荐阅读更多精彩内容

  • --- layout: post title: "如果有人问你关系型数据库的原理,叫他看这篇文章(转)" date...
    蓝坠星阅读 775评论 0 3
  • 1、通过CocoaPods安装项目名称项目信息 AFNetworking网络请求组件 FMDB本地数据库组件 SD...
    阳明先生_x阅读 15,967评论 3 119
  • 当你走上不一样的道路, 你才能看到和别人不一样的风景。 世界上找不到两片一样叶子 世界上也找不到两个一样的人 世界...
    自由蒲公英阅读 230评论 0 0
  • 今天是世界读书日,愿你在自己存在的地方成为一束光,照亮世界的一角。 ——向着光亮那方 你的生命最重要的目...
    六点书社阅读 245评论 0 1
  • 昨晚我又梦到他了 我们在教室一样的屋子里面,大家都在讨论着什么,我一心想要去跟翔桑讨论,没有注意他一直跟着我。我找...
    翔酱的溜肩阅读 201评论 0 0