MySQL中事务的实现 - Sanarous的博客

MySQL中事务的实现

不同的存储引擎对事务有不同的实现方式,并且对事务的支持程度也不一样。MySQL 中支持事务的存储引擎有 InnoDB 和 NDB,InnoDB 在 MySQL 5.6.x 版本后是默认的存储引擎,所以以下均以 InnoDB 为前提,MySQL 默认的隔离级别是 RR,并且在 RR 级别下通过 MVCC 解决不可重复读问题,再加上间隙锁(也就是并发控制)解决了幻读问题,因此 InnoDB 的 RR 级别其实实现了串行化效果,并且并发性能十分优异。事务的隔离性是通过锁实现,而事务的原子性、一致性和持久性则是通过事务日志来实现。

所以就不得不提 InnoDB 中的两个十分重要的事务日志:redo log 和 undo log。

redo log

在 InnoDB 中,事务日志通过 redo log(重做日志)和 InnoDB Log Buffer(日志缓存区)实现,后者在磁盘中,而前者是在内存中。当事务开启时,事务中的操作会写入 Buffer 中,在事务提交之前,这些缓冲的日志都需要提前刷新到磁盘上持久化。

当事务提交之后,在 Buffer Pool 中映射的数据文件才会慢慢刷新到磁盘,此时如果数据库崩溃或者宕机,那么当系统重启进行恢复时,就可以根据 redo log 中记录的日志,把数据库恢复到崩溃前的一个状态,未完成的事务也可以继续提交,也可以选择回滚,这基于恢复的策略而定。

系统启动的时候,就已经为 redo log 分配了一块连续的存储空间,以顺序追加的方式记录 redo log,通过顺序 I/O 来改善性能,所有的事务共享 redo log 的存储空间,它们的 redo log 按语句的执行顺序,依次交替的记录在一起。

我们还是用老套的存款和理财为例,假设有这么一张表:

然后我们依次执行以下 SQL 语句:

1
2
3
4
5
6
7
start transaction;
select balance from bank where name="zhangsan";
// 生成 重做日志 balance=600
update bank set balance = balance - 400;
// 生成 重做日志 amount=400
update finance set amount = amount + 400;
commit;

根据上述分析,上述事务语句在 redo log 中执行步骤和后台操作就是这样的:

所以 redo log 有什么作用呢?

因为 MySQL 为了提升性能不会把每次的修改都实时同步到磁盘,而是会先存储到 Buffer Pool 中,然后通过后台线程去做缓冲池和磁盘之间的同步。那么问题来了,如果还没来得及同步,发生了崩溃或者宕机,还没来得及执行上图中红色的操作,这样就会导致丢失部分已提交事务的修改信息!所以引入了 redo log 来记录已经成功提交事务的修改信息,并且会把 redo log 持久化到磁盘,系统重启之后再读取 redo log 恢复最新数据。

undo log

undo log 主要为事务的回滚服务,用于记录数据被修改前的信息,与 redo log 相反,undo log 主要记录的是数据的逻辑变化,为了在发生错误时回滚之前的操作,需要将之前的操作都记录下来,然后在发生错误时才可以回滚。

还是以上面那个为例:

所以每次写入数据或者修改数据之前都会把修改前的信息记录到 undo log。假如由于系统错误或者 rollback 操作回滚的话,可以根据 undo log 的信息进行回滚到没被修改前的状态。

所以,redo log其实保障的是事务的持久性和一致性,而 undo log 则保障了事务的原子性,事务的隔离性是通过读写锁 + MVCC 来实现的。
如果这篇文章对您很有帮助,不妨
-------------    本文结束  感谢您的阅读    -------------
0%