浅析数据库事务的隔离性(isolation)

数据库事务ACID

数据库事务可以被定义为一个或者几个数据库允许的操作的集合。这个集合需要支持ACID特性。

在ACID特性中,隔离性(isolation)指的是不同事务在提交的时候,最终呈现出来的效果是串行的,换句话说,既是不同事务,按照提交的先后顺序执行,再换句话说,对于事务本身来说,它所感知的数据库,应该只有它自己在操作。那么最简单的方法,我们按照定义来实现数据库事务的隔离性即可,很明显这在同时并发有多个事务要执行的环境下是没有执行效率的,一个事务的执行,必然会阻塞其他事务的执行。所以SQL的标准制定者对此作出妥协,提出隔离性的四个等级,其中最高级隔离等级才是串行执行。在没有到达串行执行等级的情况下,事务又是并发执行的,总是或多或少会存在问题,这在后面的例子当中会讲到。

隔离性的四个等级

  • 未提交读(read uncommitted)这个等级是最低等级,也可以认为,事务之间完全不隔离,事务A开始一个事务,接着事务B开始,事务B对数据C继续update,这时候,A读取了B未提交(commit)的数据,这种情况叫做脏读(dirty read)。这个时候要是事务B遇到错误必须rollback,那么A读取的数据就完全是错的。可以想象这样完全不隔离的状态下,我们相对于数据库的业务方程序员写的一个sql,提交个db的执行引擎,返回的结果是多么不可确定啊。

  • 提交读(read committed)既然读取别的事务未提交的数据很不安全,那么在上一个等级完全裸奔的情况下,增加一个要求:事务读取的数据,都是别的事务已经提交了的。但是只要在还没达到串行执行的情况下,总会有问题的,事务A select了一条数据,接着事务B update 这条数据,然后commit,这时候A还未提交,A再回来读这条数据,发现数据居然变了,按照我们之前所说,我们的目标是:对于一个事务本身来说,它所感知的数据库,应该只有它自己在操作,那么A会觉得自己并没有更新数据啊,怎么数据突然变了,这种情况叫做 不可重复读(Non-repeatable reads)

  • 可重复读(repeatable read)可重复读,即是在上一个级别的基础上,保证不会在一个事务内两次select同一条数据会出现变化,即是别的事务对你select的对象进行update操作不会影响。但是,如果是insert操作,在这个隔离级别还是会受到影响。事务A开启事务,并select一段有范围的数据,然后事务B开启事务,在先前A事务select的那段有范围的数据中insert一条数据,然后提交事务,接着事务A再select出来这段数据,发现数据多了一条,这种情况叫幻读(Phantom Read)

  • 序列化读(serializable)这也就是最高级别,保证事务之间不会有任何踩踏,每个事务都可以认为只有它自己在操作数据库。

隔离性的实现

我们知道,如果要实现数据库事务最高隔离性,也就是最安全的隔离性,有个显而易见的实现就是当一个事务在执行的时候,其他全部事务都阻塞,等待这个事务执行完再执行,这在现代多核CPU环境下显然非常浪费计算资源。为了充分利用资源,必须支持并发,这里就涉及并发控制(Concurrency control)
这是一个非常大的主题,关系到数据库,有两个比较重要的方法,一个是用锁(lock),一个是称为多版本并发控制(MVCC)的方法。

通过锁的方式来实现隔离性

读写锁

读写锁的概念很平常,当你在读取数据的时候,应该先加读锁,读取完之后的某个时间再解开读锁,那么加了读锁的数据,应该需要有什么特性呢,应该只能读,不能写,因为加了读锁,说明有事务准备读取这个数据,如果被别的事务重写这个事务,那数据就不准确了。所以一个事务给这个数据加了读锁,别的事务也可以对这个数据加读锁,因为大家都是只读不写。

写锁则具有排他性(exclusive lock),当一个事务准备对一个数据进行写操作的时候,先要对数据加写锁,那么数据就是可变的,这时候,其他事务就无法对这个数据加读锁了,除非这个写锁释放。

两端式提交锁(Two-phase locking)

两段式提交分为两步:

  1. 这个阶段只加锁,或者释放锁(读写锁)
  2. 这个阶段只会释放锁

下面对应于不同隔离级别对加锁方式进一步分析:

  • 未提交读(read uncommitted):这个级别加锁,其实并不需要用两端式加锁,每一个具体操作执行完,锁就可以释放了。

  • 提交读(read committed):这个阶段其实也可以按照每个操作执行前加锁,执行之后释放锁的方式。

  • 可重复读(repeatable read) : 这个级别,就要求读锁必须,到事务结束前最后时刻才能释放,这样才能保证读取到数据是不可变的,可重复读的。但是这样会阻塞其他事务对加锁的数据的写操作。

  • 序列化读(serializable):这个级别要求,两段式提交的第一步,要在事务开始的时候,原子性的把需要的锁全部加好(这显然很难估算,除非很大力度的锁),在事务结束前最后时刻,把全部锁一次性释放。这样做的结果就是使很多数据在事务执行期能都被加锁,无法被其他事务所使用。

使用多版本并发控制(MVCC)

加锁的方式处理事务一个比较大的问题就是会造成死锁(dead lock),原因就是一个事务加锁的数据并不止只有一行。事务A对行C加写锁,事务B对行D加写锁,接着事务希望获取行D的锁,事务B希望获取行C的锁,这样很容就死锁了。

使用MVCC就可以避免很多情况下的加锁操作,使用数据冗余的方式来实现事务隔离(这真是个很好的设计啊)

什么是MVCC

MVCC提供的只是一种思路,具体的实现比较多样化。大体的思路是每一行保存冗余数据,读写的时间戳,也可以称为版本号,在对某一行数据继续update或者delete的时候,并不直接操作,而是复制多一份副本进行操作,这个就是所谓多版本(multiversion)

mysql innodb对于MVCC的实现

innodb对每一行保存两个系统版本号,一个更新操作的版本号,一个删除操作的版本号,这两个版本号的来源是事务的ID(transaction id),也就是说,当某个事务对这一行数据进行update,或者删除的时候,相应会把它的事务ID写入这行数据的更新操作的版本号,删除操作的版本号中。

事务ID是随时间推移而增长,而且不可重复的。一个事务打开之后:

  • 对于select操作:每次只会select具有比当前事务ID更小的更新操作版本号的数据,而且这些数据要保证删除版本号为空,或者删除版本号大于当前事务ID。
  • 对于update操作:对该行数据复制出一份副本,同时在更新操作版本号写入当前事务ID,同时把当前事务ID写入之前的删除操作的版本号中。
  • 对于insert操作:写入新行,同时在更新操作版本号写入当前事务ID
  • 对于delete操作:在删除操作版本号写入当前事务ID

mysql官方innodb的实现是用MVCC,官方声称默认的innodb的隔离级别是可重复读。但是mysql是保证不会出现幻读的,因为它每次select只会读取在事务开始时候的snapshot,并且忽略在这个时刻之后提交的所有变更。consistent read

mysql官方文档提到串行隔离级别要在原来的基础说对每一个select操作执行SELECT ... LOCK IN SHARE MODE
这样就可以读取的数据加读锁了,那么其他试图写入数据都必须阻塞。那么就可以实现序列化串行了。

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

推荐阅读更多精彩内容