MySQL事务(Transaction)详解(二)——事务日志

一、 序言

针对突然宕机的问题:MySql不会自动继续执行,不会自动直接回滚,但是可以人工手动选择继续执行或者直接回滚,依据是事务日志。

事务开启时,事务中的操作,都会先写入存储引擎的日志缓冲中,在事务提交之前,这些缓冲的日志都需要提前刷新到磁盘上持久化,这就是人们口中常说的“日志先行”(Write-Ahead Logging)

为了最大程度避免数据写入时io瓶颈带来的性能问题,MySQL采用了这样一种缓存机制:当query修改数据库内数据时,InnoDB先将该数据从磁盘读取到内存中,修改内存中的数据拷贝,并将该修改行为持久化到磁盘上的事务日志(先写redo log buffer,再定期批量写入),而不是每次都直接将修改过的数据记录到硬盘内,等事务日志持久化完成之后,内存中的脏数据可以慢慢刷回磁盘,称之为Write-Ahead Logging。事务日志采用的是追加写入,顺序io会带来更好的性能优势。

innodb事务日志包括redo log和undo log。redo log是重做日志,提供前滚操作,undo log是回滚日志,提供回滚操作。

二、undo log 和 redo log

  • 概念
    在数据库系统中,事务的原子性和持久性是由事务日志(transaction log)保证的,在实现时也就是上面提到的两种日志,前者用于对事务的影响进行撤销,后者在错误处理时对已经提交的事务进行重做,它们能保证两点:
    1、发生错误或者需要回滚的事务能够成功回滚(原子性);
    2、在事务提交后,数据没来得及写会磁盘就宕机时,在下次重新启动后能够成功恢复数据(持久性);
    在数据库中,这两种日志经常都是一起工作的,我们可以将它们整体看做一条事务日志,其中包含了事务的 ID、修改的行元素以及修改前后的值。
    在这里插入图片描述

  • redo log (重做日志)——保障的是事务的持久性和一致性

    为了避免脏数据刷回磁盘过程中,掉电或系统故障带来的数据丢失问题,InnoDB采用事务日志(redo log)来解决该问题。

    在事务 提交之后,数据没有来得及 写入 到 磁盘 的时候,mysql 重启 就会执行 redo log 重做日志

    redo log通常是物理日志,记录的是数据页的物理修改,而不是某一行或某几行修改成怎样怎样,它用来恢复提交后的物理数据页(恢复数据页,且只能恢复到最后一次提交的位置)。

  • undo log (回滚日志)——保障了事务的原子性,主要为事务的回滚服务

    执行过程中发生异常,错误,手动回滚,都是执行的undo日志。事务执行完了 mysql 是不会 再去执行 事务回滚日志。

    undo用来回滚行记录到某个版本。undo log一般是逻辑日志,根据每行记录进行记录。

  • 默认 存放位置(可以修改mysql.ini 进行配置)
    redo log => ib_logfile*
    undo log => ibdata*

  • 几个相关命令

    查看事务日志 :show engine innodb status\G;
    查看日志文件设置状态:show variables like ‘innodb_%’;

三、redo log和undo log的过程分析

eg : 假设有2个数值,分别为A和B,值为1,2
    1 start transaction;
    2 记录 A=1 到undo log;
    3 update A = 3;
    4 记录 A=3 到redo log;
    5 记录 B=2 到undo log;
    6 update B = 4;
    7 记录B = 4 到redo log;
    8 将redo log刷新到磁盘
    9 commit

在1-8的任意一步系统宕机,事务未提交,该事务就不会对磁盘上的数据做任何影响.
如果在8-9之间宕机,恢复之后可以选择回滚,也可以选择继续完成事务提交,因为此时redo log已经持久化若在9之后系统宕机,内存映射中变更的数据还来不及刷回磁盘,那么系统恢复之后,可以根据redo log把数据刷回磁盘

四、undo log工作原理

在数据修改的时候,不仅记录了redo,还记录了相对应的undo,如果因为某些原因导致事务失败或回滚了,可以借助该undo进行回滚。

想要保证事务的原子性,就需要在异常发生时,对已经执行的操作进行回滚,而在 MySQL 中,恢复机制是通过回滚日志(undo log)实现的,所有事务进行的修改都会先记录到这个回滚日志中,然后在对数据库中的对应行进行写入。

注意:系统发生崩溃、数据库进程直接被杀死后,当用户再次启动数据库进程时,还能够立刻通过查询回滚日志将之前未完成的事务进行回滚,这也就需要回滚日志必须先于数据持久化到磁盘上,是我们需要先写日志后写数据库的主要原因。

在日志文件中:在事务中使用的每一条 INSERT 都对应了一条 DELETE,每一条 UPDATE 也都对应一条相反的 UPDATE 语句。
回滚日志工作原理
如上所示 可以认为当insert一条记录时,undo log中会记录一条对应的delete记录,反之亦然,当update一条记录时,它记录一条对应相反的update记录。当执行rollback时,就可以从undo log中的逻辑记录读取到相应的内容并进行回滚。

五、redo log工作原理

redo log包括两部分:一是内存中的日志缓冲(redo log buffer),该部分日志是易失性的;二是磁盘上的重做日志文件(redo log file),该部分日志是持久的。

Redo log记录的是新数据的备份。在事务提交前,只需要将Redo log持久化,不需要将数据数据持久化。当数据库系统崩溃时,虽然数据没有持久化,但是Redo log已经持久化了。在系统从崩溃中恢复时,可以根据Redo log中的内容,将所有的数据恢复到最新的状态。

在这里插入图片描述
如上图所示:当我们在一个事务中尝试对数据进行写时,它会先将数据从磁盘读入内存,并更新内存中缓存的数据,然后生成一条重做日志并写入重做日志缓存,当事务真正提交时,MySQL 会将重做日志缓存中的内容刷新到重做日志文件,再将内存中的数据更新到磁盘上,图中的第 4、5 步就是在事务提交时执行的。

六、事务日志流程

  • MySQL的checkpoint:

    checkpoint,即检查点。在undolog中写入检查点,表示在checkpoint前的事务都已经完成commit或者rollback了,也就是检查点前面的事务已经不存在数据一致性的问题了

  • mysql事务日志

    Innodb的事务日志是指Redo log,简称Log,保存在日志文件ib_logfile里面(去mysql数据目录下看下)。Innodb还有另外一个日志Undo log,但Undo log是存放在共享表空间里面的(ibdata*文件,存储的是check point日志序列号)。

  • 流程
    在这里插入图片描述
    如上图所示,Innodb的一条事务日志共经历4个阶段:

       1、创建阶段:事务创建一条日志;
       2、日志刷盘:日志写入到磁盘上的日志文件; (ib_logfile里面)
       3、数据刷盘:日志对应的脏页数据写入到磁盘上的数据文件;
       4、写CKP:日志被当作Checkpoint写入日志文件;(ib_data里面
    

七、innodb_flush_log_at_trx_commit 参数解析

  • 概念

    innodb_flush_log_at_trx_commit 用来配置flush log到磁盘的时机。具体就是从log buffer写到log file,并写入到磁盘上的时机。

  • 操作命令

    -- 查看日志文件设置状态:
    show variables like 'innodb_%';  
    -- 更改:
    set @@global.innodb_flush_log_at_trx_commit = 0; -- 0,1,2
    show variables like 'innodb_flush_log_at_trx_commit';
    
  • 对比

    0:(延迟写): log_buff --每隔1秒–> log_file —实时—> disk
    1:(实时写,实时刷): log_buff —>实时—> log_file —实时—> disk–>系统默认
    2:(实时写,延迟刷): log_buff —实时—> log_file --每隔1秒–> disk
    在这里插入图片描述

  • 性能测试对比

    性能的检测办法:
    设置innodb_flush_log_at_trx_commit 不同的值,然后对比 所用的时间
    在这里插入图片描述

  • 数据问题对比

    因为在实际过程中很难去模拟实际的断点故障的情况,但是你可以尝试通过终止MySQL服务来测试;数据的插入速度是很快的(大量数据除外),为了方便测试这里可以对于数据插入之后睡眠一下,然后再去终止MySQL服务;就可以模拟情况;过程需要自己返回测试过程 cmd 运行程序 然后 执行数据的过程中终止MySQL服务
    在这里插入图片描述
    具体三个参数的优缺点,在上一篇文章,已经做了详细说明,这里不在赘述。

事务不会自动回滚

特别注意:在程序中使用了事务之后,如果在事务提交之前程序发生了异常,事务是不会自动回滚的。因此在程序中使用事务时,一般用try catch 捕获异常,来实现手动回滚。

总结两点

1、在事务操作过程中***没有commit***,但是服务器异常终止,如果服务器重启会执行undo log 日志,把数据还原为初始状态。

2、事务在操作过程中commit,但是还没来得及把数据写入到磁盘中,服务器异常终止,此时就由redo log决定了。

redo log 由两部分组成:
一个是内存中的日志缓冲(redo log buffer),该部分日志是易失性的;
二是磁盘上的重做日志文件(redo log file),该部分日志是持久的

此时要 配合这个参数 innodb_flush_log_at_trx_commit(如下图):
在这里插入图片描述

发布了61 篇原创文章 · 获赞 11 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/dmguying/article/details/99707668