MVCC 就是多版本并发控制,一句话概括:读不加锁,写不挡读。InnoDB 靠它让普通 SELECT 读到一个“快照”,不用等别的事务提交。
实现就三样东西。每行记录带两个隐藏字段:DB_TRX_ID 记最后改这行的事务号,DB_ROLL_PTR 指向 undo log 里的旧版本;undo log 存着这行被改之前的数据;Read View 是读的时候生成的一份“当前活跃事务名单”,拿来判断某个版本对自己可不可见。
可见性判断:版本的事务号比 Read View 里最小活跃事务还小,说明早就提交了,能看;落在活跃名单里说明还没提交,不能看;比最大事务号还大说明是之后才开的事务,也不能看。看不到就顺着 DB_ROLL_PTR 往 undo log 里翻上一个版本,直到翻到能看的为止。
两种读法要分清:普通 SELECT 是快照读,走 MVCC;SELECT ... FOR UPDATE、UPDATE、DELETE 是当前读,读最新版本并加锁。
隔离级别决定快照什么时候生成:READ COMMITTED 每次快照读都新建 Read View,所以能看到别人刚提交的数据;REPEATABLE READ 只在事务第一次快照读时建一次,整个事务都用它,这就是“可重复读”的来源。
-- 事务 A
START TRANSACTION;
SELECT * FROM t WHERE id = 1; -- 快照读,拿到事务开始时的版本
-- 期间事务 B 改了这一行并提交,A 再查还是老数据(REPEATABLE READ 下)
SELECT * FROM t WHERE id = 1;
-- 当前读,能看到最新已提交数据,并加锁
SELECT * FROM t WHERE id = 1 FOR UPDATE;
COMMIT;