MySQL事务原理浅析

前言

​ 由于本身对数据的可靠性,可用性方面特别感兴趣,因此在MySQL事务方面看了不少资料,也看了不少博客,因此想到本身也写一篇博客整理整理本身所学内容,尽可能用本身的语言解释得通俗易懂。
<!-- more -->数据库

事务经典场景

​ 在不少介绍事务的博客都会代入这样一个场景,先简单说说:安全

​ 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 锁。ci

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

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

时间 取款事务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版

相关文章
相关标签/搜索