理解MySQL的MVCC
我们为什么需要 MVCC ? 没有MVCC(多版本并发控制 Multi-Version Concurrency Control)的情况下,面对数据库事务中"读某一条数据"的行为,我们只能加锁。这个锁可以是共享锁/排他锁,保证"我读的时候没人过来写",避免脏读(读到和最终结果不一致的数据)。然而一旦加锁,其他线程就要等,一等并发效率就直线下降。 MVCC 的思路是:读的时候不加锁,而是通过"版本"来判断一条数据对我来说能不能读。 每一行数据的三个隐藏字段ba 每一条数据除了业务字段外,还有三个隐藏字段: DB_TRX_ID 最近一次插入/更新/删除该行的事务id DB_ROLL_PTR 回滚指针,指向这行数据在 undo log 中的上一个版本 DB_ROW_ID 隐藏主键,仅在表没有定义主键时才会生成 undo log 把同一行数据的历史版本串成一条链,DB_ROLL_PTR 就是链上的指针,出问题时可以顺着它一路往回找老版本。 Read View:读之前先拍个快照 在真正读数据之前,我们会先生成一个快照,叫做 read view。它记录了这几样东西: m_ids 生成快照那一刻,所有"活跃"(未提交)的事务id列表 min_trx_id m_ids 里最小的那个id max_trx_id 系统里下一个将要分配的事务id(也就是目前已知最大事务id + 1) 注意 max_trx_id 不是"当前活跃事务里最大的那个",而是"还没被任何事务用过的、未来第一个可用的id"——因为事务id是全局递增分配的。 这里要先说清楚一件容易被忽略的事:read view 什么时候生成,在不同隔离级别下是不一样的。 READ COMMITTED(读已提交):每次执行 SELECT 语句都重新生成一个 read view REPEATABLE READ(可重复读,MySQL默认):只在事务内第一次执行 SELECT 时生成一次,之后整个事务复用同一个 read view 这个区别直接决定了"我这次读,能不能看到别人事务提交的新数据"——RC 每次都能看到最新提交的结果,RR 则始终锁定在事务开始时的那个快照上,这也是可重复读名字的由来。 拿到 read view 之后,怎么判断某一行能不能读 现在回到最初的问题:我们要读一行数据,它的 DB_TRX_ID = 200。这条数据被事务200修改过,我们能不能读到它,要按顺序做以下判断: ...