Skip to content

Latest commit

 

History

History
39 lines (33 loc) · 4.69 KB

MySQL如何保证事务的ACID特性-持久性(Durability).org

File metadata and controls

39 lines (33 loc) · 4.69 KB

MySQL如何保证事务的ACID特性-持久性(Durability)

说明

持久性:一旦事务提交,则其所做的修改就会永久保存到数据库中。此时即使系统崩溃,修改的数据也不会丟失。(就是说事务一旦提交,对数据库的改变是永久的)。

当事务已经被提交之后,就无法再次回滚了,唯一能够撤回已经提交的事务的方式就是创建一个相反的事务对原操作进行『补偿』,这也是事务持久性的体现之一。

重做日志实现事务的持久性

与原子性一样,事务的持久性也是通过日志来实现的,MySQL使用重做日志实现事务的持久性,重做日志由两部分组成,一是内存中的重做日志缓冲区,因为重做日志缓冲区在内存中,所以它是易失的,另一个就是在磁盘上的重做日志文件,它是持久的。

Buffer Pool

InnoDB作为MySQL的存储引擎,数据是存放在磁盘中的,但如果每次读写数据都需要磁盘IO,效率会很低。为此,InnoDB提供了缓存(Buffer Pool),Buffer Pool中包含了磁盘中部分数据页的映射,作为访问数据库的缓冲:当从数据库读取数据时,会首先从Buffer Pool中读取,如果Buffer Pool中没有,则从磁盘读取后放入Buffer Pool;当向数据库写入数据时,会首先写入Buffer Pool,Buffer Pool中修改的数据会定期刷新到磁盘中(这一过程称为刷脏)。

Buffer Pool的使用大大提高了读写数据的效率,但是也带了新的问题:如果MySQL宕机,而此时Buffer Pool中修改的数据还没有刷新到磁盘,就会导致数据的丢失,事务的持久性无法保证。

redo log

redo log被引入来解决Buffer Pool可能导致数据丢失的问题:当数据修改时,除了修改Buffer Pool中的数据,还会在redo log记录这次操作;当事务提交时,会调用fsync接口对redo log进行刷盘。如果MySQL宕机,重启时可以读取redo log中的数据,对数据库进行恢复。redo log采用的是WAL(Write-ahead logging,预写式日志),所有修改先写入日志,再更新到Buffer Pool,保证了数据不会因MySQL宕机而丢失,从而满足了持久性要求。

对于redo log是有两阶段的:commit和prepare,如果不使用“两阶段提交”,数据库的状态就有可能和用它的日志恢复出来的库的状态不一致。

在 InnoDB中,重做日志都是以512字节的块的形式进行存储的,同时因为块的大小与磁盘扇区大小相同,所以重做日志的写入可以保证原子性,不会由于机器断电导致重做日志仅写入一半并留下脏数据。

为什么redo log比直接将Buffer Pool中修改的数据写入磁盘(即刷脏)要快呢?

主要有以下两方面的原因:

  • 刷脏是随机IO,因为每次修改的数据位置随机,但写redo log是追加操作,属于顺序IO。
  • 刷脏是以数据页(Page)为单位的,MySQL默认页大小是16KB,一个Page上一个小修改都要整页写入;而redo log中只包含真正需要写入的部分,无效IO大大减少。

binlog 和 redo log

在MySQL中还存在binlog(二进制日志)也可以记录写操作并用于数据的恢复,至于和redo log的区别如下:

  • 层次:redo log是InnoDB引擎特有的,server层的叫binlog(归档日志)
  • 内容:redolog是物理日志,记录“在某个数据页上做了什么修改”;binlog是逻辑日志,是语句的原始逻辑
  • 写入:redolog循环写且写入时机较多,binlog追加且在事务提交时写入

redo log 和 undo log

在MySQL中,有回滚日志(undo log)和重做日志(redo log);前者用于对事务的影响进行撤销,后者在错误处理时对已经提交的事务进行重做,它们能保证两点:

  • 发生错误或者需要回滚的事务能够成功回滚(原子性);
  • 在事务提交后,数据没来得及写磁盘就宕机时,在下次重新启动后能够成功恢复数据(持久性);

在MySQL中,这两种日志经常都是一起工作的,我们可以将它们整体看做一条事务日志,其中包含了事务的 ID、修改的行元素以及修改前后的值。一条事务日志同时包含了修改前后的值,能够非常简单的进行回滚和重做两种操作。