MySQL系列4|MySQL的MVCC原理

MVCC(Multi Version Concurrency Control的简称),代表多版本并发控制。与MVCC相对的,是基于锁的并发控制(Lock-Based Concurrency Control)。
MVCC主要是为了提高数据库的并发性能,读不加锁,读写不冲突。在读多写少的OLTP应用中,读写不冲突是非常重要的,极大的增加了系统的并发性能。

1 当前读和快照读

1. 1 当前读

当前读读指取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁。比如以下这些操作都是一种当前读:

 //共享锁
  select lock in share mode;
  select for update;
 //排他锁
  update; 
  insert;
  delete;
1. 2 快照读

快照读是不加锁的非阻塞读,基于 MVCC 实现,目的是提高并发性能的。快照读读到的并不一定是数据的最新版本,而有可能是之前的历史版本。

简单说 MVCC 就是为了实现读-写冲突不加锁,而这个读指的就是快照读, 而非当前读,当前读实际上是一种加锁的操作,是悲观锁的实现。快照读的前提是隔离级别不是串行级别(串行级别下的快照读会退化成当前读)。一般的select语句都是快照读。

2 事务的隔离性

在MySQL InnoDB中,支持四种事物隔离级别

  • READ UNCOMMITED(未提交读)
    使用查询语句不会加锁,允许脏读,事务能够看到其他事务没有提交的修改,当另一个事务又回滚了修改后的情况,被称为脏读(Dirty Read)。

  • READ COMMITED(提交读):只能读取到已经提交的数据,只对记录加记录锁,而不会在记录之间加间隙锁,所以允许新的记录插入到被锁定记录的附近,所以再多次使用查询语句时,可能得到不同的结果,称为不可重复读(Non-Repeatable Read)。

  • REPEATABLE READ(可重复读):多次读取同一范围的数据会返回第一次查询的快照,会返回相同的数据行。可重复读中可能出现第二次读到而第一次没有读到的数据,也就是被其他事务插入的数据,这种情况称为幻读(Phantom Read)

  • SERIALIZABLE(串行读):完全串行化的读,每次读都需要获得表级共享锁,读写相互都会阻塞。

隔离级别         脏读    不可重复读    幻读    概念
READ UNCOMMITED √       √            √
READ COMMITTED  ×       √            √   
REPEATABLE READ ×       ×            √  
SERIALIZABLE    ×       ×            ×  

大多数数据库系统的默认隔离级别都是READ COMMITTED(RC),而MySQL的默认事务隔离级别是REPEATABLE READ(RR) ,RR解决幻读是靠锁和MVCC机制。

3 undo日志

insert undo log

代表事务在 insert 新记录时产生的 undo log, 只在事务回滚时需要,并且在事务提交后可以被立即丢弃。

update undo log

事务在进行 update 或 delete 时产生的 undo log ; 不仅在事务回滚时需要,在快照读时也需要;所以不能随便删除,只有在快速读或事务回滚不涉及该日志时,对应的日志才会被 purge 线程统一清除。

对 MVCC 有帮助的实质是 update undo log ,undo log 实际上就是存在 rollback segment 中的旧记录链。

4 MVCC原理

4.1隐藏字段

MySQL中,在每一行记录中除了数据字段,还有一些隐藏字段:


image.png
  • DB_ROW_ID:单调递增的行 ID,没定义主键时,InnoDB会以row_id为主键生成一个聚集索引。
  • DB_TRX_ID:事务ID:记录了新增/最近修改这条记录的事务id,事务id是自增的。
  • DB_ROLL_PTR:回滚指针:指向当前记录的上一个版本(在 undo log 中)。

4.2 版本链

在修改数据的时候,会向 undo log 记录数据原来的快照,用于回滚事务。
多个事务操作同一行数据时,不同事务对该行数据的修改会产生多个版本,然后通过回滚指DB_ROLL_PTR连成一个链表,这个链表就称为版本链。


image.png

4.2 ReadView

ReadView就是事务执行SQL语句时,产生的读视图。

注意,ReadView是与SQL绑定的,而并不是事务,所以即使在同一个事务中,每次SQL启动时构造的ReadView(up_trx_id和low_trx_id)也都是不一样的

核心的数据结构

trx_id_t: 每个读写事务都会通过全局id产生器产生一个id,只读事务的事务id为0,只有当其切换为读写事务时候再分配事务id。
trx_sys_t: 这个结构体用来维护系统的事务信息,全局只有一个,在数据库启动的时候初始化。比较重要的字段有:

  • max_trx_id:这个字段表示系统当前还未分配的最小事务id,如果有一个新的事务,直接把这个值作为新事务的id,然后这个字段递增即可。
  • descriptors:这个是一个数组,里面存放着当前所有活跃的读写事务id,当需要开启一个readview的时候,就从这个字段里面拷贝一份,用来判断记录的对事务的可见性。

read_view_t:
InnDB为了判断某条记录是否对当前事务可见,需要对此记录进行可见性判断,这个结构体就是用来辅助判断的。
当需要一个一致性读的时候(即创建新的readview时),会把全局读写事务id拷贝一份到readview本地(read_view_t->descriptors),当做当前事务的快照。read_view_t中包含了3个重要的属性:

  • up_limit_id:read_view_t->descriptors中最小的值,所有小于此值的记录都应该被此readview看到,可以理解为low water mark。
  • low_limit_id:创建read_view_t时的max_trx_id,其一定大于read_view_t->descriptors中的最大值,所有大于等于此值的记录都不应该被此readview看到,可以理解为high water mark。
  • trx_ids:read_view_t->descriptors的事务id列表,即Read View初始化时当前未提交的事务列表。
    image.png
可见性判断逻辑

当查询出一条记录后(记录上有一个trx_id,表示这条记录最后被修改时的事务id),可见性判断的逻辑如下:

  1. 如果记录上的trx_id小于read_view_t->up_limit_id,则说明这条记录的最后修改在readview创建之前,因此这条记录可以被看见。

  2. 如果记录上的trx_id大于等于read_view_t->low_limit_id,则说明这条记录的最后修改在readview创建之后,因此这条记录不可以被看见。

  3. 如果记录上的trx_id在read_view_t->up_limit_idread_view_t->low_limit_id之间,分两种情况:

  • trx_id在read_view_t->descriptors,则表示这条记录的最后修改是在readview创建之时,被另外一个活跃事务所修改,所以这条记录也不可以被看见。
  • trx_id不在read_view_t->descriptors之中,则表示这条记录的最后修改在readview创建之前,所以可以看到。

当进行RR读的时候,除了事务自己做的变更外,trx_ids中的事务对于本事务是不可见的。

可见性算法

Read View遵循一个可见性算法,主要是将要被修改的数据的最新记录中的 DB_TRX_ID(即当前事务 ID )取出来,如果 DB_TRX_ID 跟 Read View 的属性做了某些比较,不符合可见性,那就通过 DB_ROLL_PTR 回滚指针去取出 Undo Log 中的 DB_TRX_ID 再比较,即遍历链表的 DB_TRX_ID(从链首到链尾,即从最近的一次修改查起),直到找到满足特定条件的 DB_TRX_ID , 那么这个 DB_TRX_ID 所在的旧记录就是当前事务能看见的最新老版本。

5 RC , RR的不同

5.1 ReadView生成

RC , RR 隔离级别的一个非常大的区别就是它们生成ReadView的时机不同。

RR下的ReadView
  • 第一次创建readview后,这个readview就会一直持续到事务结束,也就是说在事务执行过程中,数据的可见性不会变,所以在事务内部不会出现不一致的情况

总结:当前事务过程中,区间[up_trx_id, low_trx_id]中的事务如果已经提交了,对当前事务是不可见的。

RC下的ReadView生成
  • 事务中的每个查询语句都单独构建一个readview,所以如果两个查询之间有事务提交了,两个查询读出来的结果就不一样

总结:当前事务过程中,区间[up_trx_id, low_trx_id]中的事务如果已经提交了,对当前事务可见

5.2 RC和RR隔离级别下的快照读和当前读:

  • RC隔离级别下,快照读和当前读结果一样,都是读取已提交的最新;
  • RR隔离级别下,当前读结果是其他事务已经提交的最新结果,快照读是读当前事务之前读到的结果。RR下创建快照读的时机决定了读到的版本。

5.3 解决幻读问题

对于快照读:通过MVCC来进行控制的,不用加锁。按照MVCC中规定的“语法”进行增删改查等操作,以避免幻读。
对于当前读:通过next-key锁(行锁+gap锁)来解决幻读问题。

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

推荐阅读更多精彩内容

  • 一、概述 MVCC: Multi-Version Concurrency Control 多版本并发控制本质上是一...
    高斯滤波函数阅读 387评论 0 1
  • 什么是 MVCC MVCC (Multiversion Concurrency Control) 中文全程叫多版本...
    AnyL8023阅读 487评论 0 0
  • 事务的4个隔离级别 读未提交 读已提交 可重复读 串行化 什么是脏读 简单说,读了一条未提交的数据 什么是不可重复...
    技术灭霸阅读 282评论 0 0
  • MVCC mvcc 指的是一种提高并发的技术。最早的数据库系统,只有读读之间可以并发,读写,写读,写写都要阻塞。引...
    我还是老油条阅读 606评论 0 1
  • 上一篇我们简单聊了聊MySQL中LRU算法的实现,那么这一篇我们聊聊MySQL的另一个重点——MVCC(多版本并发...
    程序员小韩阅读 2,384评论 0 1