概述
本文会说明mysql InnoDB引擎如何实现RC和RR
预备概念
网上资料很多,我这里描述个大概。主要为下面“看图说话”的涉及到的基础知识做个铺垫
mvcc
多版本控制器,我们知道数据库每一条记录都是启用事务执行的操作。每个记录的值 背后一定有操作的事务ID。
多次更新就会有多个。(事务开始的时候,会和InnoDb的事务系统申请,这个trxId是严格递增的)
所以上面描述下来,心里有个图,每行记录都有一个 row list(每个row记录之前的值和trxId 可以做前滚和后滚)。
两阶段锁
行锁是需要的时候加上,事务结束的时候释放
基于这个原则,我们开发的时候 如果一个大事务里面有涉及加锁的操作,可以放在靠近退出事务的地方
减少并发引起的锁等待时间
看图说话
K初始值=1,我们分析下ABC三个事务在RC和RR下的不同值
宝典规则(重点)
事务启动的时候,会记录一个数组,里面包含所有活跃的事务ID(活跃=生成但是未提交的事务),
还涵盖了一个最大水位值(最大活跃trxId + 1)
不在这个数组里面的 一般是可见的,如果在这个范围内 是自己的那个事务 也是可见
RR分析
事务A读到的K=1,事务B读到的K=3
1.事务开始的时候,会生成一个视图(类似一个快照,之后的别的事务提交 我不care),一致性读主要靠这个实现
用上面说的那个就是row trxId要小于数组里面最小值,其他的我不可见 不管了
靠这一点很好解释事务A读到的k=1,启动的时候B和C都没执行commit
2.但是按1说的有个奇怪的地方B在update的时候,应该是没法读到事务C的更新后的值的。
这里说明下,每次更新操作会读最新值(不然会覆盖别的事务的更新),这里还有一个绕的地方
事务C的更新操作先执行,事务B执行更新的时候C还没有提交,这个时候B等待最新读的记录。一致要等到C提交释放了写锁,才能拿到值
还有两个操作,执行查询加S锁或者X锁 也会当前读
select * from t where id = 1 lock in share mode;
select * from t where id = 1 for update;
RC分析
事务A读到的k=1,事务B读到的K=3
每次sql执行前都会建一个视图
补充几个有意思的场景
更新一万条数据
第一种,直接执行 delete from T limit 10000; (大事务,锁的时间长 导致主从延迟)
第二种,在一个连接中循环执行 20 次 delete from T limit 500;(优)
第三种,在 20 个连接中同时执行 delete from T limit 500;(锁冲突)