January 17, 2019
事务就是要保证一组数据库操作,要么全部成功,要么全部失败
SQL 标准的事务隔离级别:
先明确一下读提交和可重复读是有区别的:
针对读提交和可重复读,MySQL 是用一致性视图来实现的:
另外,读未提交和串行化不需要使用视图。读未提交,每次读拿到的都是最新值,不需要视图。串行化是通过加锁来实现的,也不需要视图
比如我们正在核对用户的支付记录和发货记录,如果这时恰好用户又进行了一次付费购买的操作,不应该影响到核对的事务,此时我们可以使用可重复读的机制:
顾名思义,多版本并发控制 (MVCC) 就是在并发场景下允许多个版本并存来解决并发问题。上面提到的一致性视图,就是通过 MVCC 实现的。
了解了什么是 MVCC,我们一起来看下 MySQL 中是如何实现 MVCC 的。
begin/start transaction 命令并不是事务的起点,在他们执行后遇到的第一个操作 InnoDB 的语句才是事务的起点。 如何马上启动一个事务?
start transaction with consistent snapshot
MySQL 中有两个视图的概念:
InnoDB 中每个事务都有一个唯一的事务 id,它是在事务开始的时候向 InnoDB 事务系统申请的,是按照申请顺序严格递增的。
在 MVCC 中,每行数据可能有多个版本,每次事务更新数据时,都会产生一个新的版本,并且把 transaction id 赋值给这个数据版本的事务 ID,多个事务同时更新一行数据时,这行数据就会对应不同的版本。
更新的时候,以最新的版本为基础进行更新,读的时候,以当前事务 ID 对应的版本去读。
所谓快照,并不是真实的创建一个同等大小的镜像数据。MySQL 会把数据的变更行为,记录到 undo log 中,需要去读旧的数据的时候,就拿最新的数据,结合 undo log 回溯到当前事务 ID 对应的数据版本。
在事务启动时,会根据事务启动的时间来看当前的最新数据,该时间之前的最新数据就是他能读到的数据,该时间之后的数据,他不会认。
(图片来自 https://time.geekbang.org/column/article/70562)
InnoDB 为每个事务分配了一个事务 ID,每一行数据,都可能会有多个版本,每个版本与一个事务 ID 对应。InnoDB 会把所有行数据的更新操作记录到 undo log 中,undo log 中还会记录对应版本的事务 ID,处于某个事务的查询在读取该行数据时,会从 undo log 中找到对应的版本,进行回退计算,得出事务开始时建立的快照的数据。
在具体实现上,InnoDB 会为每个事务构造一个数组,用来保存在该事务启动时,当前正在活跃的所有事务 ID,活跃就是指启动了但还没提交。数组中,ID 的最小值叫做低水位,ID 的最大值 + 1 表示为高水位。
得到一致性视图的方式就变成了对几种情况的判断:
可重复读的核心: 当事务更新数据时,只能用当前读,如果当前行的记录被其他事务锁定,就需要进入锁等待。
https://time.geekbang.org/column/article/70562 这个专栏很不错,值得订阅!
痕迹
没有过去,就没法认定现在的自己