通俗理解 mysql 的mvcc机制

本文详细解析了MVCC(多版本并发控制)的工作原理及其核心组件ReadView的实现细节。通过具体的例子介绍了ReadView的可见性规则及如何确定数据版本的可见性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

mvcc (多版本并发控制),开启多个事务,并基于快照读Read View实现;

Read View几个属性
    trx_ids: 当前系统活跃事务版本号集合;
    low_limit_id: 创建当前read view 时, 系统最大事务版本号+1;
    up_limit_id: 创建当前read view 时,  活跃事务最小版本号;
    creator_trx_id: 创建当前read view的事务版本号;

read_view  可见性规则:
 1.数据事务ID小于read view中的最小活跃事务ID,即trx_id<up_limit_id,

   说明数据是在当前事务生成read view之前就已经存在了,则显示
   
 2.数据事务ID大于或等于read view 中的当前系统的最大事务ID,即trx_id>low_limit_id,

    说明该数据是在当前read view 创建之后才产生的,则不显示
 
 3.trx_id = creator_trx_id,是本事务产生的数据,则显示
 4.被访问版本的trx_id,在readview的min_trx_id和max_trx_id之间,则需要判断trx_id值是否在m_ids列表中
    如果在:说明创建readview时,该版本的事务还未提交,通过回滚指针查找历史版本并返回;
    如果不在:说明创建readview时,该版本的事务已经提交,所以直接返回该版本的数据

结合具体列子,
准备一张表,字段为id,age,当我们插入一条数据的时候,mysql会增加几个隐藏字段,不会展示给我们用户,其中一个字段为事务id,即trx_id
准备三个事务,分别更新数据,trx_id是隐藏的,这里列出来方便说明

idage trx_idpoint
事务1111指针
事务2122指针
事务3133指针

    注:这个不是表数据,事务提交或者回滚后,只有一条


首先,开启事务1,插入一条数据id=1,age=1 这时隐藏列trx_id的值假如为1,以后的事务在此基础上依次增长,提交事务,这时数据库里就有一条数据了。

情景1
      开启事务2,执行查询操作,
      那么trx_id=2,mysql会记录开启是这个事务时的事务数据,包括目前存活的事务id列表trx_ids,值为2
      最大事务id ,活跃的最大事务id+1,low_limit_id 为3;
      最小事务id,up_limit_id 为2;
      自己的事务版本,creator_trx_id为2

   开启事务2执行查询全表的操作,表中生成id为1的数据的事务id为1,即trx_id 1<2,显示id=1的这条数据;

情景2
   开启事务2,事务执行查询之前,开启了事务3,并更新为id=1,age=3,提交完成,此时事务2查询,id=1的这条数据的trx_id为3,即trx_id 3>=3,不会显示age=3的这条数据,通过回滚直接读取历史版本,显示age为1的数据

情景3
   开启事务2,事务3,事务3执行更新,未提交,
   事务2执行查询操作,活动的事务trx_ids列表为(2,3),low_limit_id为4,up_limit_id为2
   age为3的记录的trx_id处于 2 <= 3 <4,3在 trx_ids中,不显示这个版本的数据;

情景4
   开启事务2,事务3,事务3执行更新并提交,
   事务2执行查询操作,活动的事务trx_ids列表为(2),low_limit_id为4,up_limit_id为2
   age为3的记录的trx_id处于 2 <= 3 <4,3不在 trx_ids中,显示这个版本的数据;

<think>好的,用户之前让我解释过脏读、不可重复读和幻读,现在他们想了解MySQL事务隔离级别。我需要确保这次回答与之前的内容衔接,同时保持通俗易懂。用户可能是刚开始学习数据库,或者需要在实际应用中处理事务问题,所以应该用生活化的例子来解释每个隔离级别,并关联到之前的问题。 首先,我需要回忆MySQL的四个隔离级别:读未提交、读已提交、可重复读、串行化。然后,针对每个级别,用简单的场景说明它们如何解决脏读、不可重复读和幻读的问题。同时,需要指出每个级别的优缺点,比如性能差异。可能用户想知道如何选择隔离级别,所以最后可以给出选择建议。 要注意之前用户的问题回复中用了存钱罐、账户余额、班级身高的例子,这次可以继续类似的场景,保持一致性。比如,用银行转账、统计人数等例子。另外,用户可能对默认隔离级别感兴趣,所以需要提到MySQL默认是可重复读,而其他数据库如Oracle可能不同。 需要确保解释清晰,避免技术术语过多,但也要准确。例如,可重复读如何避免幻读,而实际上在MySQL中通过Next-Key Locking实现,但用户可能不需要深入细节,只要知道结果即可。最后用表格对比总结,帮助用户记忆。</think>用银行存取款的例子,轻松理解MySQL的四种事务隔离级别: --- ### **1. 读未提交(Read Uncommitted)** **场景**:ATM机显示你的余额是500元,这时别人正在给你转账(操作未完成),ATM直接显示转账中途的余额(比如转账中的1000元),但对方突然取消转账,你看到的钱其实是假的。 **特点**: - 性能最好,但可能看到**脏数据**(别人未提交的操作) - 相当于没有隔离,容易引发脏读、不可重复读、幻读 **代码设置**: ```sql SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; ``` --- ### **2. 读已提交(Read Committed)** **场景**:ATM机只显示已完成的转账金额。第一次查余额是500元,期间别人成功转给你200元,你再查就看到700元。 **特点**: - 解决脏读,但**同一事务中两次查询结果可能不同**(不可重复读) - 多数数据库的默认级别(如Oracle) **代码设置**: ```sql SET TRANSACTION ISOLATION LEVEL READ COMMITTED; ``` --- ### **3. 可重复读(Repeatable Read)** **场景**:你开启事务查看余额是500元,之后即使别人转账成功,你在这个事务中每次查都是500元(像事务开始时拍了快照)。 **特点**: - **解决脏读和不可重复读** - **可能仍有幻读**(但MySQL通过Next-Key Locks解决了幻读) - MySQL的默认隔离级别 **代码设置**: ```sql SET TRANSACTION ISOLATION LEVEL REPEATABLE READ; ``` --- ### **4. 串行化(Serializable)** **场景**:ATM机被锁住,一次只允许一个人操作,其他人必须排队等待。 **特点**: - 通过锁表实现完全隔离,**解决所有并发问题** - **性能最差**,实际很少使用 **代码设置**: ```sql SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; ``` --- ### **隔离级别对照表** | **隔离级别** | 脏读 | 不可重复读 | 幻读 | 性能 | |--------------------|------|------------|------|-------| | 读未提交(Read Uncommitted) | ❌可能 | ❌可能 | ❌可能 | ⚡最快 | | 读已提交(Read Committed) | ✅避免 | ❌可能 | ❌可能 | ⚡快 | | 可重复读(Repeatable Read) | ✅避免 | ✅避免 | ✅避免* | 🐢中等 | | 串行化(Serializable) | ✅避免 | ✅避免 | ✅避免 | 🐢最慢 | > *注:MySQL的"可重复读"通过MVCC+间隙锁实际解决了幻读 --- ### **如何选择?** 1. **默认选可重复读**(MySQL优化较好,兼顾性能和安全) 2. **对数据一致性要求极高**(如金融系统)→ 串行化 3. **只追求速度,允许暂时数据错误**(如实时性高的统计)→ 读未提交 4. **平衡选择**(多数OLTP系统)→ 读已提交 **通俗记忆**: - 读未提交 → **裸奔模式** - 读已提交 → **穿件外套** - 可重复读 → **开美颜相机** - 串行化 → **进保险箱**
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值