MySQL InnoDB 并发控制,事务的实现 学习笔记

所在文集:数据库


如何保证数据一致性

通过并发控制保证数据一致性的常见手段有:

  • 锁(Locking)
  • 数据多版本(Multi Versioning)

如何使用普通锁保证一致性?

  • 操作数据前,锁住,实施互斥,不允许其他的并发任务操作;
  • 操作完成后,释放锁,让其他任务执行;

普通锁存在什么问题?
简单的锁住太过粗暴,连“读任务”也无法并行,任务执行过程本质上是串行的。

于是出现了共享锁排他锁

  • 共享锁(Share Locks,记为S锁),读取数据时加 S 锁
    • 共享锁之间不互斥,简记为:读读可以并行
  • 排他锁(eXclusive Locks,记为X锁),修改数据时加 X 锁
    • 排他锁与任何锁互斥,简记为:写读,写写不可以并行
    • 可以看到,一旦写数据的任务没有完成,数据是不能被其他任务读取的,这对并发度有较大的影响

有没有可能,进一步提高并发呢?
即使写任务没有完成,其他读任务也可能并发,这就引出了数据多版本
数据多版本是一种能够进一步提高并发的方法,它的核心原理是:

  • 写任务发生时,将数据克隆一份,以版本号区分;
  • 写任务操作新克隆的数据,直至提交;
  • 并发读任务可以继续读取旧版本的数据,不至于阻塞;
数据多版本

如上图:

  • 最开始数据的版本是 V0;
  • T1 时刻发起了一个写任务,这是把数据克隆了一份,进行修改,版本变为 V1,但任务还未完成;
  • T2 时刻并发了一个读任务,依然可以读 V0 版本的数据;
  • T3 时刻又并发了一个读任务,依然不会阻塞;

可以看到,数据多版本,通过“读取旧版本数据”能够极大提高任务的并发度。

提高并发的演进思路,就在如此:

  • 普通锁,本质是串行执行
  • 读写锁,可以实现读读并发
  • 数据多版本,可以实现读写并发

undo log

日志文件,记录数据被 修改前 的值,用来进行 rollback回滚。
如某个事务 T1,将 X 的值由 5 修改为 10,则 undo log 写入 <T1,X,5>
对于 insert 操作,undo log 记录新数据的 PK(ROW_ID),回滚时直接删除;

为什么要有 undo log?
数据库事务未提交时,会将事务修改数据的镜像(即修改前的旧版本)存放到 undo log 里,当事务回滚时,或者数据库奔溃时,可以利用 undo log,即旧版本数据,撤销未提交事务对数据库产生的影响。

一句话,undo日志用于保障,未提交事务不会对数据库的ACID特性产生影响。

磁盘上 不存在 单独的 undo log 文件,所有的 undo log 均存放在主 ibd 数据文件中(表空间)。
记录先写入到 undo buffer,但当缓冲满的时候,undo buffer 中的内容会也会被刷新到磁盘。

redo log

日志文件,记录数据被 修改后 的值,回来恢复未写入到磁盘文件的已成功事务更新的数据。
如某个事务 T1,将 X 的值由 5 修改为 10,则 redo log 写入 <T1,X,10>

为什么要有 redo log?
数据库事务提交后,必须将更新后的数据刷到磁盘上,以保证 ACID 特性。磁盘随机写性能较低,如果每次都刷盘,会极大影响数据库的吞吐量。
优化方式是,将修改行为先写到 redo log 里,再定期将数据刷到磁盘上,这样能极大提高性能。
假如某一时刻,数据库崩溃,还没来得及刷盘的数据,在数据库重启后,会重做 redo log 日志里的内容,以保证已提交事务对数据产生的影响都刷到磁盘上。

一句话,redo log 用于保障,已提交事务的 ACID 特性。

磁盘上 存在 单独的 redo log 文件。
记录先写入到 redo buffer,但当缓冲满的时候,redo buffer 中的内容会也会被刷新到磁盘。

rollback segment 回滚段

回滚段。在 InnoDB 中,undo log 被划分为多个段,具体某行的 undo log 就保存在某个段中,称为回滚段。

事务的过程

假设 User 表有两个字段 <name, salary>
InnoDB 为每行记录都实现了三个隐藏字段:

  • 6 字节,单调递增的行 ID(DB_ROW_ID
  • 6 字节,记录每一行最近一次修改它的事务 ID(DB_TRX_ID
  • 7 字节,回滚指针(DB_ROLL_PTR),记录指向回滚段 undo log 的指针

因此实际的 User 表有五个字段 <name, salary, ID, DB_TRX_ID, DB_ROLL_PTR>

假设某一行的初始值为:<Tom, 10000, 1, NULL, NULL>

假设某一个事务里执行了 update users set salary = 20000 where name = 'Tom',存储引擎会依次进行如下操作:

  • 排他锁 锁定该行。
  • 记录 redo log,即被 修改后 的值 <Tom, 20000>
  • 记录 undo log,即被 修改前 的值 <Tom, 10000, 1, NULL, NULL>
  • 修改当前数据表中的值,变为 <Tom, 20000, 1, 01, DB_ROLL_PTR>,其中 DB_ROLL_PTR 指向之前 undo log 中的那一行

若 提交了 事务
只需要更改事务状态为 COMMIT 即可,不需做其他额外的工作。

若 回滚了 事务
需要根据当前 DB_ROLL_PTR 回滚指针 从 undo log 中找出事务修改前的版本,并恢复。

一个具体的示例

数据表 t(id PK, name)
数据为:

1, shenjian
2, zhangsan
3, lisi

此时没有事务未提交,故回滚段是空的。

接着启动了一个事务,并且事务处于未提交的状态。

start trx;
delete (1, shenjian);
update set(3, lisi) to (3, xxx);
insert (4, wangwu);

可以看到:

  • 被删除前的 (1, shenjian) 作为旧版本数据,进入了回滚段;
  • 被修改前的 (3, lisi) 作为旧版本数据,进入了回滚段;
  • 被插入的数据 PK(4) 进入了回滚段;

接下来,假如事务 rollback,此时可以通过回滚段里的 undo log 回滚。



可以看到:

  • 被删除的旧数据恢复了;
  • 被修改的旧数据也恢复了;
  • 被插入的数据,删除了;

InnoDB 是基于多版本并发控制的存储引擎

InnoDB 是高并发互联网场景最为推荐的存储引擎,根本原因,就是其多版本并发控制(Multi Version Concurrency Control, MVCC)。行锁,并发,事务回滚等多种特性都和 MVCC 相关。
MVCC就是通过“读取旧版本数据”来降低并发事务的锁冲突,提高任务的并发度。

InnoDB为何能够做到这么高的并发?
回滚段里的数据,其实是历史数据的快照(snapshot),这些数据是不会被修改,select 可以肆无忌惮的并发读取他们。

快照读(Snapshot Read),这种一致性不加锁的读(Consistent Nonlocking Read),就是 InnoDB 并发如此之高的核心原因之一。

这里的一致性是指,事务读取到的数据,要么是事务开始前就已经存在的数据(当然,是其他已提交事务产生的),要么是事务自身插入或者修改的数据。

什么样的 select 是快照读?
除非显示加锁,普通的select语句都是快照读,例如:
select * from t where id>2;

这里的显示加锁,非快照读是指:
select * from t where id>2 lock in share mode;
select * from t where id>2 for update;

总结:

  • 常见并发控制保证数据一致性的方法有锁,数据多版本;
  • 普通锁串行,读写锁读读并行,数据多版本读写并行;
  • redo log 保证已提交事务的 ACID 特性,设计思路是,通过顺序写替代随机写,提高并发;
  • undo log 用来回滚未提交的事务,它存储在回滚段里;
  • InnoDB 是基于 MVCC 的存储引擎,它利用了存储在回滚段里的 undo log,即数据的旧版本,提高并发;
  • InnoDB 之所以并发高,快照读不加锁;
  • InnoDB 所有普通 select 都是快照读;

引用:
Mysql InnoDB MVCC 实现原理
InnoDB并发如此高,原因竟然在这?

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

推荐阅读更多精彩内容