当前位置: 首页 > news >正文

MVCC的实现原理

文章目录

  • 前言
  • 什么是MVCC?
  • 为什么需要MVCC?
    • 传统锁机制的问题
    • MVCC的优势
  • MVCC的实现原理
    • 核心组件
      • 1. 隐藏字段
      • 2. Undo Log(回滚日志)
      • 3. Read View(读视图)
    • 版本可见性判断
  • MVCC在不同隔离级别下的表现
    • READ COMMITTED(读已提交)
    • REPEATABLE READ(可重复读)
  • MVCC的局限性
    • 1. 幻读问题
    • 2. 写写冲突需要锁

前言

为什么在MySQL中,一个事务在读取数据的时候,另一个事务同时修改同一条数据,读事务却不会被阻塞?这背后的功臣就是MVCC

什么是MVCC?

MVCC(多版本并发控制),是一种并发控制方法,主要用于数据库管理系统中,同时为事务提供对数据库的并发访问。MVCC让数据库可以为同一行数据维护多个版本,不同的事务可以看到不同版本的数据,从而实现不加锁的情况下解决读写冲突。

为什么需要MVCC?

传统锁机制的问题

在没有MVCC之前,数据库主要依靠锁来解决并发问题:

这种方式存在明显问题:

  • 性能低下:读写操作互相阻塞
  • 并发度差:大量读操作会严重影响写操作
  • 死锁风险:复杂的锁竞争容易产生死锁

MVCC的优势

MVCC带来的好处:

  • 读写并发:读操作不会阻塞写操作,写操作也不会阻塞读操作
  • 高并发:支持更多的并发事务
  • 无死锁:读操作永远不会死锁
  • 一致性读:保证事务读取数据的一致性

MVCC的实现原理

核心组件

MVCC主要依靠以下几个组件实现:

1. 隐藏字段

InnoDB为每行记录添加了几个隐藏字段:

  • DB_TRX_ID:最后修改该行的事务ID
  • DB_ROLL_PTR:指向undo log中该行的上一个版本
  • DB_ROW_ID:单调递增的行ID(当表没有主键时使用)

2. Undo Log(回滚日志)

Undo Log记录了数据的历史版本,形成一个版本链。

3. Read View(读视图)

Read View是事务开始时的数据库快照,包含:

  • m_ids:当前活跃事务ID列表
  • min_trx_id:活跃事务中最小的事务ID
  • max_trx_id:下一个要分配的事务ID
  • creator_trx_id:创建该Read View的事务ID

版本可见性判断

当事务读取数据时,需要判断某个版本是否对当前事务可见:

// 伪代码:版本可见性判断
public boolean isVisible(long trx_id, ReadView readView) {if (trx_id == readView.creator_trx_id) {// 是当前事务修改的,可见return true;}if (trx_id < readView.min_trx_id) {// 在所有活跃事务之前提交的,可见return true;}if (trx_id >= readView.max_trx_id) {// 在Read View创建之后开启的事务,不可见return false;}if (readView.m_ids.contains(trx_id)) {// 在活跃事务列表中,不可见return false;}// 已提交的事务,可见return true;
}

MVCC在不同隔离级别下的表现

READ COMMITTED(读已提交)

在RC隔离级别下,每次SELECT都会创建新的Read View:

特点:

  • 每次读取都创建新的Read View
  • 能读到其他事务已提交的修改
  • 解决了脏读,但存在不可重复读问题

REPEATABLE READ(可重复读)

在RR隔离级别下,同一事务中的所有SELECT使用同一个Read View:

特点:

  • 事务开始时创建Read View,整个事务期间复用
  • 保证了可重复读
  • MySQL默认隔离级别

MVCC的局限性

1. 幻读问题

MVCC无法完全解决幻读问题:

-- 事务A
BEGIN;
SELECT * FROM users WHERE age > 18;  -- 返回5条记录-- 事务B
INSERT INTO users (name, age) VALUES ('Charlie', 25);
COMMIT;-- 事务A
SELECT * FROM users WHERE age > 18;  -- 仍然返回5条记录(MVCC保证)-- 但是...
UPDATE users SET name = CONCAT(name, '_updated') WHERE age > 18;
-- 会更新6条记录!包括事务B插入的记录

2. 写写冲突需要锁

MVCC只解决了读写冲突,写写冲突仍需要锁:

-- 两个事务同时更新同一行
-- 事务A
BEGIN;
UPDATE users SET name = 'Alice' WHERE id = 1;  -- 获得行锁-- 事务B
BEGIN;
UPDATE users SET name = 'Bob' WHERE id = 1;    -- 被阻塞,等待行锁
http://www.dtcms.com/a/313060.html

相关文章:

  • git配置公钥/密钥
  • Android XR SDK深度解析:构建下一代沉浸式体验的开发指南
  • 《从原理到实践:MySQL索引优化与SQL性能调优全解析》
  • Vue中:deep()和 ::v-deep选择器的区别
  • JavaScript:编程世界中的“语盲”现象
  • Java,八股,cv,算法——双非研0四修之路day24
  • ulimit参数使用详细总结
  • ELECTRICAL靶机
  • Transformer模型用于MT信号相关性预测与分析
  • python的易物小店交换系统
  • 2106. 摘水果
  • 数据结构中使用到的C语言
  • RocksDb 是什么?levelDB、LSM 树、SSTable又分别是什么?区别呢?
  • Linux 内存调优之如何限制进程、系统级别内存资源
  • 第二章 矩阵
  • 剥离petalinux设备树,使用 dtc 单独编译
  • 主流身份认证协议都有哪些?应用场景有何区别?
  • BRL贝叶斯规则列表
  • 《C++》stack容器详解
  • 块三角掩码(Block-Triangular Masking)
  • Remix框架:高性能React全栈开发实战
  • 安卓加固脱壳
  • js--2048小游戏
  • C++23 Concepts:用类型约束重构泛型编程的终极方案
  • 构造类型--结构体,共同体联合体,枚举
  • 【AI论文】Rep-MTL:释放表征级任务显著性在多任务学习中的潜力
  • 影响人类发音的疾病类型种类和数据集
  • CMake 命令行参数完全指南(2)
  • 界面规范4-按钮
  • All the Mods 9 - To the Sky - atm9sky 局域网联机报错可能解决方法