begin/start transaction 命令并不是一个事务的起点,在执行到它们之后的第一个操作 InnoDB 表的语句,事务才真正启动。如果你想要马上启动一个事务,可以使用 start transaction with consistent snapshot 这个命令。
序言:
- MySQL InnoDB 存储引擎的默认支持的隔离级别是 REPEATABLE-READ(可重复读)。可重复读的核心就是一致性读(consistent read)。
可以通过
SELECT @@tx_isolation;
来查看。
begin/start transaction
命令并不是一个事务的起点,在执行到它们之后的第一个操作 InnoDB 表的语句,事务才真正启动。
start transaction with consistent snapshot;
的意思是从这个语句开始,创建一个持续整个事务的一致性读快照。在读提交隔离级别下,
start transaction with consistent snapshot;
等效于普通的start transaction
。
MySQL “视图”的概念
-
一个是 view。它是一个用查询语句定义的虚拟表,在调用的时候执行查询语句并生成结果。创建视图的语法是 create view … ,而它的查询方法与表一样。
-
另一个是 InnoDB 在实现 MVCC 时用到的一致性读视图,即 consistent read view,用于支持 RC(Read Committed,读提交)和 RR(Repeatable Read,可重复读)隔离级别的实现。
MVCC 里的“快照”
在可重复读隔离级别下,事务在启动的时候就“拍了个快照”。
举例说明:
结果是:事务 B 查到的 k 的值是 3,而事务 A 查到的 k 的值是 1。
快照的实现方式使用 transaction id & row trx_id 来共同实施,参考如下行状态:
所以,数据表中的一行记录,其实可能有多个版本 (row),每个版本有自己的 row trx_id。
高低水位
undo log(回滚日志)如上图的虚线所示,并且旧版本数据值在物理上不存在,而在每次需要的时候计算得来的。(高低水位的计算) 答:InnoDB 利用了“所有数据都有多个版本”的这个特性,实现了“秒级创建快照”的能力。
视图数组
这里,事务 A 的视图数组如下图:
参考视图数组里面的,事务 transaction id ,可以得知,事务 A 查询结果确实为 1。
更新逻辑
更新数据都是先读后写的,而这个读,只能读当前的值(即使是在不可见的高水位也需要是最新值),称为“当前读”(current read)。
所以这个读和查询可能不同,当前读为高水位也可,查询只能查询当前可见值。 当前读,总是读取已经提交完成的最新版本(注意排他锁哦!!)。
当前读
而如果上面事务 A 加锁去读的话,也可以读到最高水位的值,查询结果为 3 。
mysql> select k from t where id=1 lock in share mode; -- 读锁(S 锁,共享锁)
mysql> select k from t where id=1 for update; -- 写锁(X 锁,排他锁)
升级版事务流程
假如事务 C 变成了事务 C'
这样的话可以考虑之前提及到的“两阶段锁协议”了。事务 C’没提交,也就是说 (1,2) 这个版本上的写锁还没释放。而事务 B 是当前读,必须要读最新版本,而且必须加锁,因此就被锁住了,必须等到事务 C’释放这个锁,才能继续它的当前读。
到这里,我们把一致性读、当前读和行锁就串起来了。