MySQL事务原理浅析 - 明白易懂

事务经典场景
​ 在很多介绍事务的博客都会代入这样一个场景,先简单说说:

​ A给B转账100,A少100,B多100。如果A少100后系统崩溃怎么办?B的钱多不了,这样金钱总数凭空少了100。这里就需要用到事务了。

什么是事务?
​ 事务是恢复和并发控制的基本单位,事务有四个特性(ACID),原子性(Atomicity),一致性(Consistency),隔离性(Isolation),持久性(Durability)。本文主要围绕这四个特性展开介绍。

原子性
​ 原子性就是不可拆分的特性,要么全部成功然后提交(commit),要么全部失败然后回滚(rollback)。若开启事务,在上述场景就不会出现 A少100 成功,B多100 失败 这种情况。MySQL通过Redo Log重做日志实现了原子性,在将执行SQL语句时,会先写入redo log buffer,再执行SQL语句,若SQL语句执行出错就会根据redo log buffer中的记录来执行回滚操作,由此拥有原子性。

一致性
​ 一致性指事务将数据库从一种状态转变为下一种一致的状态。比如有一个字段name有唯一索引约束,那么在事务前后都不能有重复的name出现违反唯一索引约束,否则回滚。在上述场景中即金钱总数总是200,不能凭空增加减少。MySQL通过undo Log实现一致性,执行SQL语句时,会先写入undo log再写入 redo log buffer。undo是逻辑日志,会根据之前的SQL语句进行相应回滚,比如之前是insert那么回滚时会执行一个delete,一个update会执行 一个相反的update。并且除了回滚,undo log还有一个作用是MVCC,当用户读取一行记录时,若该记录已经被其他事务占用,当前事务可通过undo读取之前的行版本信息,实现非锁定读取。并且undo log也会产生redo log,因为undo log也需要持久性的保护。

隔离性
​ 首先介绍如果没有隔离性会发生的4种情况

丢失更新
​ A事务撤销时,把已经提交的B事务的更新数据覆盖了。这种错误可能造成很严重的问题,通过下面的账户取款转账就可以看出来,MySQL通过三级封锁协议的第一级解决了丢失更新,事务 T 要修改数据 A 时必须加 X 锁,直到 T 结束才释放锁。

时间取款事务A转账事务B
T1开始事务 
T2 开始事务
T3查询账户余额为1000元 
T4 查询账户余额为1000元
T5 汇入100元把余额改为1100元
T6 提交事务
T7取出100元把余额改为900元 
T8撤销事务 
T9余额恢复为1000 元(丢失更新) 


脏读
​ 脏读主要是读取到了其他事务的数据,而其他事务随后发生回滚。MySQL通过三级封锁协议的第二级解决了脏读,在一级的基础上,要求读取数据 A 时必须加 S 锁,读取完马上释放 S 锁。

时间取款事务A转账事务B
T1 开始事务 
T2 开始事务
T3查询账户余额为1000元 
T4  
T5 汇入100元把余额改为1100元
T6查询账户余额为1100元(脏读) 
T7 撤销事务
T8汇入100元以为是1200元 


不可重复读
​ 不可重复读是读取到数据后,随后其他事务对数据发生了修改,无法再次读取。MySQL通过三级封锁协议的第三级解决了不可重复读。在二级的基础上,要求读取数据 A 时必须加 S 锁,直到事务结束了才能释放 S 锁。

时间取款事务A转账事务B
T1开始事务 
T2 开始事务
T3查询账户余额为1000元 
T4  
T5 汇入100元把余额改为1100元
T6查询账户余额为1100元(不可重复读) 
T7 提交事务
T8提交事务 


幻读
​ 幻读是读取到数据后,随后其他事务对数据发生了新增,无法再次读取。在InnoDB引擎Repeatable Read的隔离级别下,MySQL通过Next-Key Lock以及MVCC解决了幻读,事务中分为当前读以及快照读。

1.快照读(snapshot read) ——通过MVCC来避免幻读

简单的select操作(不包括 select … lock in share mode, select … for update)

2.当前读(current read) ——通过Next-Key Lock 来避免幻读 Next-Key Lock即间隙锁(Gap Lock)+行锁 (Record Lock)

select … lock in share mode

select … for update

insert

update

delete

时间取款事务A 转账事务B
T1开始事务  
T2  开始事务 
T3查询账户余额为1000元 RMB 100元美元 
T4  
T5 汇入100欧元
T6 查询账户余额为1000元 RMB 100元美元 100欧元(幻读) 
T7 提交事务
T8提交事务 

Read Uncommitted
​ 解决了丢失更新

Read Committed
​ 解决了丢失更新+脏读

Repeatable Read
​ 解决了丢失更新+脏读+不可重复读 (Innodb下也解决了幻读,原理上文已说明)

Serializable
​ 解决了丢失更新+脏读+不可重复读+幻读

从上至下,性能越差,安全性越优。

持久性
一旦事务提交,则其所做的修改就会永久保存到数据库中。此时即使系统崩溃,修改的数据也不会丢失。具体实现原理就是在事务commit之前会将,redo log buffer中的数据持久化到硬盘中的redo log file,这样在commit的时候,硬盘中已经有了我们修改或新增的数据,由此做到持久化。

总结
​ 简单总结了一下MySQL事务,对于Redo Undo没有做到了如指掌的掌握所以介绍篇幅不太大,随着学习深入以后会进行相应补充。

参考资料
—–《MySQL技术内幕 InnoDB存储引擎》 第2版
--------------------- 
作者:寻筝 
来源:CSDN 
原文:https://blog.csdn.net/gotobat/article/details/80351224 
版权声明:本文为博主原创文章,转载请附上博文链接!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值