使用场景

  • update users set name=’XXX’ where id = 10

    流程

  • Buffer Pool(缓冲池):如果对应‘id=10’的数据在缓冲池中,则直接使用;否则,从磁盘上加载该数据

  • 为这一行数据加上独占锁
  • Undo log:讲更前的旧数据写入undo log文件,用于事物回滚
  • 更新缓冲池中的数据
  • Redo Log Buffer:将数据的修改日志写入该缓冲区,记录的是“对哪个数据页中的的什么记录,做了什么修改”。用于mysql宕机恢复
    • 此时这条redo log日志是处于prepare状态
    • redo log根据配置的刷盘策略进行落盘
  • server层执行器生成该操作的binlog,并写入binlog日志
  • server层提交事务,这时候会调用引擎的提交事务接口,将刚刚的redolog日志标记为commit,并写入redo log file

    两阶段提交

    两阶段含义

  • innodb写入的redolog日志状态为prepare状态,

  • 通知server层可以提交事务了,server层写入对应的binlog日志
  • 提交事务
  • innodb层将redolog状态更新为commit,并触发redolog buffer的刷盘策略

    作用

  • 保证某一时刻数据库的状态,和crash后用日志恢复出来的数据库状态一致

  • 保证主从库数据一致

    mysql异常重启,保证数据完整性

  • 写入redo log处于prepare状态,写bin log之前crash

    • 事务回滚;binlog没写,所以也不会同步到从库
  • 写入redolog 处于prepare状态,写binlog后,还未将redo log改为commit
    • 如果redo log日志有commit标志,则直接提交
    • 如果redo log是prepared标志,则判断对应的binlog是否存在并完整
      • 如果是,则提交
      • 否,回滚事务
  • 如何判断binlog是否完整

    • 一个事务的binlog是有完整格式的
      • statement格式的binlog,最后会有COMMIT
      • row格式的binlog,最后会有一个XID event
      • mysql5.6.2后引入了一个binlog-checksum参数,用来验证binlog的正确性
    • 同一个事务的binlog和redo log有一个共同的参数:XID

      Redo Log刷盘策略

      参数:innodb_flush_log_at_trx_commit

  • 值为0

    • 提交事物后,不会把redo_log_Buffer中的数据刷新到磁盘
    • 事物提交成功,但mysql宕机会导致数据丢失
  • 值为1
    • 提交事物后,把redo_log_Buffer中的数据刷新到磁盘
    • 事物提交成功后宕机,不会丢失内存中的数据,因为重启后mysql会根据redo log恢复数据
  • 值为2

    • 提交事物,会把redo log buffer中数据写入到磁盘缓冲区OS cache中,OS cache中数据会被异步落盘
    • 此情况下,也可能会丢失数据

      后台定时任务

  • InnoDB存储引擎有一个后台线程,每隔1秒,就会把redo log buffer中的内容写到文件系统缓存(page cache),然后调用fsync刷盘。

  • 除了后台线程每秒1次的轮询操作,还有一种情况,当redo log buffer占用的空间即将达到innodb_log_buffer_size一半的时候,后台线程会主动刷盘

    修改后的缓存页直接落盘和修改写入redo log文件区别

    缓存页落盘

  • 一次刷盘就是16KB比较耗时,但你一次修改可能就只改了几个字节,性价比不高

  • 缓存页刷盘是随机写,性能差

    写redo log文件

  • 一行redo log日志可能就只占用几十字节,就包含表空间、数据页号、文件偏移量、更新值等信息

  • 顺序写速度快

    redo log结构

  • 日志类型(就是类似MLOG_1BYTE之类的),表空间ID,数据页号,数据页中的偏移量,具体修改的数据

    日志类型

  • MLOG_1BYTE:指修改了一个字节

  • MLOG_2BYTE:修改了两个字节
  • 。。。
  • MLOG_8BYTE:修改了8个字节
  • MLOG_WRITE_STRING:插入或修改了一大串的值

    Redo log block

  • 存放多行的redo log日志

  • 一个redo log block是512字节
  • 写redo log时候,会现在内存中创建一个redo log block的数据结构,写满后将其写入磁盘

    结构

    Redo Log - 图1

    redo log buffer

  • mysql 启动后申请的一块连续的内存区域,将其划分为多个redo log block区块

  • redo log buffer的内存大小通过参数innodb_log_buffer_size指定,默认大小16MB
  • 写redo log其实就是写入redo log buffer中的某个redo log block
  • 通常一个事物会包含多行redo log,这多行redo log就是一组,当这一事物多个操作都执行成功后,才会一起写入block

    刷盘时机

  • 写入redo log buffer的日志,已经占了redo log buffer总容量的一半,就会写入磁盘

  • 一个事物提交,必须将其redo log所在的redo log block刷入磁盘
  • 后台定时任务每隔1秒,将redo log buffer中的日志写到操作系统的page cache中,然后调用fsync落盘
  • mysql关闭时候,进行落盘

    磁盘文件

  • redo log磁盘文件目录:又innodb_log_group_home_dir指定,通过show variables like ‘datadir’查看

  • innodb_log_files_in_group指定redo log文件数量(默认2个),innodb_log_file_size指定每个redo log的大小(默认48MB)
  • 循环写
  • redo log写满后,需要刷脏页落盘

    组提交