MVCC机制和间隙锁

MVCC机制:多版本并发控制,通过保留数据的多个版本来提高并发性能。隔离级别:可重复读

核心实现原理

  • 隐藏字段:给每行数据加了2个隐藏字段【最后一次修改该行的事务id和指向改行历史版本的回滚指针
  • undo log(回滚日志):记录了修改之前的数据内容【注意与RedoLog区别,一个是记录修改前,一个是记录修改后。回滚和持久化】
  • Read View(读视图):ReadView是MVCC中用于定义事务可见性的机制。每个事务在开始时会生成一个ReadView,ReadView决定了哪些版本的数据对该事务可见。ReadView包含以下关键信息:
  • m_ids:当前活跃(未提交)的事务ID集合。
  • min_trx_id:活跃事务中的最小事务ID。
  • max_trx_id:下一个将要分配的事务ID。
  • creator_trx_id:创建该ReadView的事务ID。
  • 通过ReadView,事务可以判断某行数据的版本是否可见:

  • 如果数据版本的事务ID小于min_trx_id,则该版本对当前事务可见。
  • 如果数据版本的事务ID在m_ids中,则该版本对当前事务不可见。
  • 如果数据版本的事务ID大于max_trx_id,则该版本对当前事务不可见。

Repeatable Read隔离级别下,MVCC可以避免快照读中的幻读问题。然而,如果事务中存在当前读操作(如SELECT ... FOR UPDATE),仅靠MVCC是不够的,还需要锁机制来防止其他事务插入新数据。

(1) 间隙锁(Gap Lock)

间隙锁锁定的是索引记录之间的“间隙”,防止其他事务在范围内插入新数据。例如,表中现有记录的age值为[10, 20, 30],执行SELECT * FROM users WHERE age > 20 FOR UPDATE时,InnoDB会锁定(20, +∞)的区间。

(2) 临键锁(Next-Key Lock)

临键锁是**记录锁(Record Lock) + 间隙锁(Gap Lock)**的组合,锁定索引记录及其之前的间隙。例如,索引值为20的记录,临键锁会锁定区间(-∞, 20]

SELECT * FROM Users WHERE age < 20 FOR UPDATE;
  • 记录锁:锁定age = 10的记录。【有记录为10的行】
  • 间隙锁:锁定age = 10和age = 20之间的间隙。
  • 临键锁:锁定(-∞, 20)范围内的所有记录和间隙。

3. 如何解决幻读

当执行范围查询并请求共享或排他锁时(如SELECT ... FOR UPDATE),InnoDB会给符合条件的已有数据记录的索引项加锁,同时也会对键值在条件范围内但并不存在的记录(即间隙)加锁。这样,其他事务就无法在这个范围内插入新的数据,从而避免了幻读问题。

例如:

  1. 事务A执行:SELECT * FROM users WHERE age > 20 FOR UPDATE,InnoDB会通过临键锁锁定age > 20的范围。
  2. 事务B尝试插入:INSERT INTO users (age) VALUES (25),该操作会被阻塞,直到事务A提交或回滚。
  3. 因此,事务A的两次查询结果一致,避免了幻读。

4. 注意事项

  • 仅对当前读有效:MVCC的快照读可以避免幻读,但若事务中混合快照读和当前读,仍需显式加锁。
  • 索引依赖:间隙锁和临键锁依赖于索引。若查询未使用索引,InnoDB会退化为表锁,严重影响性能。
  • 隔离级别限制:在Read Committed隔离级别下,间隙锁会被禁用,无法完全避免幻读。

总结

InnoDB通过MVCC的快照读临键锁的当前读双重机制,在Repeatable Read隔离级别下解决了幻读问题:

  • MVCC:保证快照读的一致性视图。
  • 临键锁:通过锁定索引范围,阻止其他事务插入新数据。

理解这些机制有助于在实际开发中合理设计事务和查询逻辑,确保数据一致性并提升并发性能。

全部评论
其实你写的这些是详细原理 当年总结一个回答模版吗
点赞 回复 分享
发布于 02-23 13:44 广东

相关推荐

想申请延毕了,找工作找到崩溃,越找就越想摆烂,还有25届的和我一样感受吗?
码农索隆:没事哒,好兄弟,慢慢来,调整心态,车到山前必有路,感到迷茫的时候,多抬头看看
点赞 评论 收藏
分享
03-31 16:42
已编辑
郑州西亚斯学院 后端
Java抽象带篮子:你简历少了几个模块看上去就感觉信息很少,简历怎么写可以看看我发的帖子
点赞 评论 收藏
分享
评论
4
15
分享

创作者周榜

更多
牛客网
牛客企业服务