目录

1 概述

2 redo日志

2.1 redo log和二进制日志的区别

2.2 redo日志的基本概念

2.3 日志块(log block)

2.4 redo log的格式

2.5  日志刷盘的规则

2.6 数据页刷盘的规则及checkpoint

2.7 innodb的恢复行为

2.8 redo log相关变量

2 undo日志

2.1 基本概念

2.2 undo日志的存储方式

2.3 undo日志相关变量

2.4 delete/update操作的内部机制

3 binlog和事务日志及group commit


1 概述

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

  • redo log通常是物理日志,记录的是数据页的物理修改,主要用来恢复提交后的物理数据页(恢复数据页,且只能恢复到最后一次提交的位置)。
  • undo一般是逻辑日志,用来回滚行记录到某个版本。

两者的作用:

  • Redo Log 保证事务的持久性
  • Undo Log 保证事务的原子性(在 InnoDB 引擎中,还用 Undo Log 来实现 MVCC)

2 redo日志

2.1 redo log和二进制日志的区别

redo日志不是二进制日志,虽然功能和二进制日志类似,比如二进制日志也能实现重做功能,主要区别如下:

(1)二进制日志在存储引擎的上层产生,且对数据库的任何修改都可产生二进制日志,而redo日志在innodb层产生,只记录该存储引擎的修改,并且二进制入职优先于redo日志。

(2)二进制日志是记录操作的逻辑语句,如某一行记录的每一列的数值,而redo日志是物理格式上的日志,记录的是数据库中每个页的修改。

(3)二进制日志是提交的时候一次性写入,而redo日志是数据准备修改前写入缓存中的redo log,然后才对缓存中数据进行修改。

(4)二进制日志是一次提交记录一次,而redo日志可能同一事务有多次记录。

2.2 redo日志的基本概念

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

在概念上,innodb通过force log at commit机制实现事务的持久性,即在事务提交的时候,必须先将该事务的所有事务日志写入到磁盘上的redo log file和undo log file中进行持久化。

为了确保每次日志都能写入到事务日志文件中,在每次将log buffer中的日志写入日志文件的过程中都会调用一次操作系统的fsync操作(即fsync()系统调用),将OS buffer中的日志刷到磁盘上的log file中。

MySQL支持用户自定义在commit时如何将log buffer中的日志刷log file中。这种控制通过变量 innodb_flush_log_at_trx_commit 的值来决定。该变量有3种值:0、1、2,默认为1。但注意,这个变量只是控制commit动作是否刷新log buffer到磁盘。

  • 当设置为1的时候,事务每次提交都会将log buffer中的日志写入os buffer并调用fsync()刷到log file on disk中。这种方式即使系统崩溃也不会丢失任何数据,但是因为每次提交都写入磁盘,IO的性能较差。
  • 当设置为0的时候,事务提交时不会将log buffer中日志写入到os buffer,而是每秒写入os buffer并调用fsync()写入到log file on disk中。也就是说设置为0时是(大约)每秒刷新写入到磁盘中的,当系统崩溃,会丢失1秒钟的数据。
  • 当设置为2的时候,每次提交都仅写入到os buffer,然后是每秒调用fsync()将os buffer中的日志写入到log file on disk。

注意,有一个变量 innodb_flush_log_at_timeout 的值为1秒,该变量表示的是刷日志的频率。

在主从复制结构中,要保证事务的持久性和一致性,需要对日志相关变量设置为如下:

  • 如果启用了二进制日志,则设置sync_binlog=1,即每提交一次事务同步写到磁盘中。
  • 总是设置innodb_flush_log_at_trx_commit=1,即每提交一次事务都写到磁盘中。

上述两项变量的设置保证了:每次提交事务都写入二进制日志和事务日志,并在提交时将它们刷新到磁盘中。

测试可知,innodb_flush_log_at_trx_commit 为0或2时,性能差距很小,但2要比0更加安全。它们都是每秒从os buffer刷到磁盘,它们之间的时间差体现在log buffer刷到os buffer上。因为将log buffer中的日志刷新到os buffer只是内存数据的转移,并没有太大的开销,所以每次提交和每秒刷入差距并不大。但如果数据量十分巨大,比如每秒数万条数据,这可能导致大量数据的丢失。更好的插入数据的做法是将值设置为1,然后修改存储过程,将每次循环都提交修改为只提交一次这样既能保证数据的一致性,也能提升性能。

2.3 日志块(log block)

innodb存储引擎中,redo log以块为单位进行存储的,每个块占512字节,这称为redo log block。所以不管是log buffer中还是os buffer中以及redo log file on disk中,都是这样以512字节的块存储的。

每个redo log block由3部分组成:日志块头、日志块尾和日志主体。其中日志块头占用12字节,日志块尾占用8字节,所以每个redo log block的日志主体部分只有512-12-8=492字节。

因为redo log记录的是数据页的变化,当一个数据页产生的变化需要使用超过492字节()的redo log来记录,那么就会使用多个redo log block来记录该数据页的变化。

日志块头包含4部分:

  • log_block_hdr_no:(4字节)该日志块在redo log buffer中的位置ID。
  • log_block_hdr_data_len:(2字节)该log block中已记录的log大小。写满该log block时为0x200,表示512字节。
  • log_block_first_rec_group:(2字节)该log block中第一个log的开始偏移位置。
  • lock_block_checkpoint_no:(4字节)写入检查点信息的位置。

关于log block块头的第三部分 log_block_first_rec_group ,因为有时候一个数据页产生的日志量超出了一个日志块,这是需要用多个日志块来记录该页的相关日志。例如,某一数据页产生了552字节的日志量,那么需要占用两个日志块,第一个日志块占用492字节,第二个日志块需要占用60个字节,那么对于第二个日志块来说,它的第一个log的开始位置就是73字节(60+12)。如果该部分的值和 log_block_hdr_data_len 相等,则说明该log block中没有新开始的日志块,即表示该日志块用来延续前一个日志块。

2.4 redo log的格式

redo log也是基于页的格式来记录的。默认情况下,innodb的页大小是16KB(由 innodb_page_size 变量控制),一个页内可以存放 非常多的log block(每个512字节),而log block中记录的又是数据页的变化。

其中log block中492字节的部分是log body,该log body的格式分为4部分:

  • redo_log_type:占用1个字节,表示redo log的日志类型。
  • space:表示表空间的ID,采用压缩的方式后,占用的空间可能小于4字节。
  • page_no:表示页的偏移量,同样是压缩过的。
  • redo_log_body表示每个重做日志的数据部分,恢复时会调用相应的函数进行解析。例如insert语句和delete语句写入redo log的内容是不一样的。

如下图,分别是insert和delete大致的记录方式。

2.5  日志刷盘的规则

log buffer中未刷到磁盘的日志称为脏日志(dirty log)。

在上面的说过,默认情况下事务每次提交的时候都会刷事务日志到磁盘中,这是因为变量 innodb_flush_log_at_trx_commit 的值为1。但是innodb不仅仅只会在有commit动作后才会刷日志到磁盘,这只是innodb存储引擎刷日志的规则之一。

刷日志到磁盘有以下几种规则:

  • 发出commit动作时。已经说明过,commit发出后是否刷日志由变量 innodb_flush_log_at_trx_commit 控制。
  • 每秒刷一次。这个刷日志的频率由变量 innodb_flush_log_at_timeout 值决定,默认是1秒。要注意,这个刷日志频率和commit动作无关。
  • 当log buffer中已经使用的内存超过一半时。
  • 当有checkpoint时,checkpoint在一定程度上代表了刷到磁盘时日志所处的LSN位置。

2.6 数据页刷盘的规则及checkpoint

内存中(buffer pool)未刷到磁盘的数据称为脏数据(dirty data)。由于数据和日志都以页的形式存在,所以脏页表示脏数据和脏日志。

      在innodb中,数据刷盘的规则只有一个:checkpoint。但是触发checkpoint的情况却有几种。在checkpoint触发后,会将buffer中脏数据页和脏日志页都刷到磁盘。

innodb存储引擎中checkpoint分为两种:

  • sharp checkpoint:在重用redo log文件(例如切换日志文件)的时候,将所有已记录到redo log中对应的脏数据刷到磁盘。
  • fuzzy checkpoint:一次只刷一小部分的日志到磁盘,而非将所有脏日志刷盘。有以下几种情况会触发该检查点:
    • master thread checkpoint:由master线程控制,每秒或每10秒刷入一定比例的脏页到磁盘
    • flush_lru_list checkpoint:从MySQL5.6开始可通过 innodb_page_cleaners 变量指定专门负责脏页刷盘的page cleaner线程的个数,该线程的目的是为了保证lru列表有可用的空闲页。
    • async/sync flush checkpoint:同步刷盘还是异步刷盘。例如还有非常多的脏页没刷到磁盘(非常多是多少,有比例控制),这时候会选择同步刷到磁盘,但这很少出现;如果脏页不是很多,可以选择异步刷到磁盘,如果脏页很少,可以暂时不刷脏页到磁盘
    • dirty page too much checkpoint:脏页太多时强制触发检查点,目的是为了保证缓存有足够的空闲空间。too much的比例由变量 innodb_max_dirty_pages_pct 控制,MySQL 5.6默认的值为75,即当脏页占缓冲池的百分之75后,就强制刷一部分脏页到磁盘。

由于刷脏页需要一定的时间来完成,所以记录检查点的位置是在每次刷盘结束之后才在redo log中标记的。

MySQL停止时是否将脏数据和脏日志刷入磁盘,由变量innodb_fast_shutdown={ 0|1|2 }控制,默认值为1,即停止时只做一部分purge,忽略大多数flush操作(但至少会刷日志),在下次启动的时候再flush剩余的内容,实现fast shutdown。

2.7 innodb的恢复行为

在启动innodb的时候,不管上次是正常关闭还是异常关闭,总是会进行恢复操作。

因为redo log记录的是数据页的物理变化,因此恢复的时候速度比逻辑日志(如二进制日志)要快很多。而且,innodb自身也做了一定程度的优化,让恢复速度变得更快。

重启innodb时,checkpoint表示已经完整刷到磁盘上data page上的LSN,因此恢复时仅需要恢复从checkpoint开始的日志部分。例如,当数据库在上一次checkpoint的LSN为10000时宕机,且事务是已经提交过的状态。启动数据库时会检查磁盘中数据页的LSN,如果数据页的LSN小于日志中的LSN,则会从检查点开始恢复。

还有一种情况,在宕机前正处于checkpoint的刷盘过程,且数据页的刷盘进度超过了日志页的刷盘进度。这时候一宕机,数据页中记录的LSN就会大于日志页中的LSN,在重启的恢复过程中会检查到这一情况,这时超出日志进度的部分将不会重做,因为这本身就表示已经做过的事情,无需再重做。

2.8 redo log相关变量

  • innodb_flush_log_at_trx_commit={0|1|2} # 指定何时将事务日志刷到磁盘,默认为1。

    • 0表示每秒将"log buffer"同步到"os buffer"且从"os buffer"刷到磁盘日志文件中。
    • 1表示每事务提交都将"log buffer"同步到"os buffer"且从"os buffer"刷到磁盘日志文件中。
    • 2表示每事务提交都将"log buffer"同步到"os buffer"但每秒才从"os buffer"刷到磁盘日志文件中。
  • innodb_log_buffer_size:# log buffer的大小,默认8M
  • innodb_log_file_size:#事务日志的大小,默认5M
  • innodb_log_files_group =2:# 事务日志组中的事务日志文件个数,默认2个
  • innodb_log_group_home_dir =./:# 事务日志组路径,当前目录表示数据目录
  • innodb_mirrored_log_groups =1:# 指定事务日志组的镜像组个数,但镜像功能好像是强制关闭的,所以只有一个log group。在MySQL5.7中该变量已经移除。

2 undo日志

2.1 基本概念

undo log有两个作用:提供回滚和多个行版本控制(MVCC)。

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

undo log和redo log记录物理日志不一样,它是逻辑日志。可以认为当delete一条记录时,undo log中会记录一条对应的insert记录,反之亦然,当update一条记录时,它记录一条对应相反的update记录。

当执行rollback时,就可以从undo log中的逻辑记录读取到相应的内容并进行回滚。有时候应用到行版本控制的时候,也是通过undo log来实现的:当读取的某一行被其他事务锁定时,它可以从undo log中分析出该行记录以前的数据是什么,从而提供该行版本信息,让用户实现非锁定一致性读取。

      undo log是采用段(segment)的方式来记录的,每个undo操作在记录的时候占用一个undo log segment

另外,undo log也会产生redo log,因为undo log也要实现持久性保护。

2.2 undo日志的存储方式

innodb存储引擎对undo的管理采用段的方式。rollback segment称为回滚段,每个回滚段中有1024个undo log segment

在以前老版本,只支持1个rollback segment,这样就只能记录1024个undo log segment。后来MySQL5.5可以支持128个rollback segment,即支持128*1024个undo操作,还可以通过变量 innodb_undo_logs自定义多少个rollback segment,默认值为128。

2.3 undo日志相关变量

主要有如下变量:

 mysql> show variables like "%undo%";
+-------------------------+-------+
| Variable_name           | Value |
+-------------------------+-------+
| innodb_undo_directory   | .     |
| innodb_undo_logs        | 128   |
| innodb_undo_tablespaces | 0     |
+-------------------------+-------+

2.4 delete/update操作的内部机制

当事务提交的时候,innodb不会立即删除undo log,因为后续还可能会用到undo log,如隔离级别为repeatable read时,事务读取的都是开启事务时的最新提交行版本,只要该事务不结束,该行版本就不能删除,即undo log不能删除。

但是在事务提交的时候,会将该事务对应的undo log放入到删除列表中,未来通过purge来删除。并且提交事务时,还会判断undo log分配的页是否可以重用,如果可以重用,则会分配给后面来的事务,避免为每个独立的事务分配独立的undo log页而浪费存储空间和性能。

通过undo log记录delete和update操作的结果发现:(insert操作无需分析,就是插入行而已)

  • delete操作实际上不会直接删除,而是将delete对象打上delete flag,标记为删除,最终的删除操作是purge线程完成的。
  • update分为两种情况:update的列是否是主键列。
    • 如果不是主键列,在undo log中直接反向记录是如何update的。即update是直接进行的。
    • 如果是主键列,update分两部执行:先删除该行,再插入一行目标行。

3 binlog和事务日志及group commit

如果事务不是只读事务,即涉及到了数据的修改,默认情况下会在commit的时候调用fsync()将日志刷到磁盘,保证事务的持久性。且在事务量非常大的时候。innodb提供了group commit功能,可以将多个事务的事务日志通过一次fsync()刷到磁盘中。

在MySQL5.6中提交事务时,在存储引擎层的上一层结构中会将事务按序放入一个队列,队列中的第一个事务称为leader,其他事务称为follower,leader控制着follower的行为。虽然顺序还是一样先刷二进制,再刷事务日志,但是机制完全改变了。

MySQL5.6中分为3个步骤:flush阶段、sync阶段、commit阶段。

  • flush阶段:向内存中写入每个事务的二进制日志。
  • sync阶段:将内存中的二进制日志刷盘。若队列中有多个事务,那么仅一次fsync操作就完成了二进制日志的刷盘操作。这在MySQL5.6中称为BLGC(binary log group commit)。
  • commit阶段:leader根据顺序调用存储引擎层事务的提交,由于innodb本就支持group commit,所以解决了因为锁 prepare_commit_mutex 而导致的group commit失效问题。

在flush阶段写入二进制日志到内存中,但是不是写完就进入sync阶段的,而是要等待一定的时间,多积累几个事务的binlog一起进入sync阶段,等待时间由变量 binlog_max_flush_queue_time 决定,默认值为0表示不等待直接进入sync,设置该变量为一个大于0的值的好处是group中的事务多了,性能会好一些,但是这样会导致事务的响应时间变慢,所以建议不要修改该变量的值,除非事务量非常多并且不断的在写入和更新。

进入到sync阶段,会将binlog从内存中刷入到磁盘,刷入的数量和单独的二进制日志刷盘一样,由变量 sync_binlog 控制。

当有一组事务在进行commit阶段时,其他新事务可以进行flush阶段,它们本就不会相互阻塞,所以group commit会不断生效。当然,group commit的性能和队列中的事务数量有关,如果每次队列中只有1个事务,那么group commit和单独的commit没什么区别,当队列中事务越来越多时,即提交事务越多越快时,group commit的效果越明显。

MySQL之 事务日志: redo和undo相关推荐

  1. 认真学习MySQL的事务日志-Redo日志

    事务有4种特性:原子性.一致性.隔离性和持久性.那么事务的四种特性到底是基于什么机制实现呢? 事务的隔离性由锁机制执行. 事务的原子性.一致性和持久性由事务的redo日志和undo日志来保证. red ...

  2. 【图文详解】MySQL事务日志 Redo log(重做) 和 Undo log(撤销)

    InnoDB Architecture https://dev.mysql.com/doc/refman/5.6/en/innodb-architecture.html 我们都知道数据库有四大属性AC ...

  3. mysql的事务日志_MySQL 事务日志

    重做日志(Redo log) 重做日志(Redo log),也叫做前滚日志,存放在如下位置,轮询使用,记录着内存中数据页的变化,在事务 ACID 过程中,主要实现的是 D(Durability)的作用 ...

  4. mysql数据库事务日志

    目录 概述 redo 日志 为什么需要redo日志 redo日志的好处特点 好处 特点 Undo日志 如何理解Undo日志 Undo日志的作用 小结 概述 事务有四种特性:原子性.一致性.隔离性和持久 ...

  5. mysql数据库事务日志已满_服务器事务日志已满解决方法

    方法一: 1.打开查询分析器,输入命令 BACKUP LOG database_name WITH NO_LOG 2.再打开企业管理器--右键要压缩的数据库--所有任务--收缩数据库--收缩文件--选 ...

  6. MySQL日志:binlog、事务日志(redo、undo)

    事务的隔离性是通过锁实现,而事务的原子性.一致性和持久性则是通过日志实现.Mysql的日志可以分为: binlog:server层实现 事务日志:包括redo log.undo log,引擎层(inn ...

  7. mysql内部实现原理面试_理解完这些基本上能解决面试中MySql的事务问题

    事务是指逻辑上的一组操作,要么都执行,要么都不执行, 事务的特性(ACID)原子性(Atomicity):事务是不可分割的工作单元,要么都成功,要么都失败, 如果事务中一个sql语句执行失败,则已执行 ...

  8. mysql事务最好别用_理解完这些基本上能解决面试中MySql的事务问题

    前言 在面试中,基本上都会问到关于数据库的事务问题,如果啥都不会或者只回答到表面的上知识点的话,那面试基本上是没戏了,为了能顺利通过面试,那MySql的事务问题就需要了解,所以就根据网上的资料总结一版 ...

  9. MySQL中事务四大特性的实现详解

    MySQL事务的四大特性的实现 基本概念 原子性实现 隔离性实现 已提交读 可重复读 持久性实现 日志文件刷新策略 基本概念 事务的四大特性ACID : 原子性Atomic : 事务的所有的SQL操作 ...

最新文章

  1. java源码推荐_基于java的推荐系统实现源代码
  2. java map 迭代删除元素,java – 如何在迭代时删除和添加元素到TreeMap?
  3. 证书格式pfx和cer的区别及转换
  4. PyTorch 1.0 中文文档:torch.distributed
  5. opencv 轮廓层次结构
  6. 虚拟机网络连接模式中桥接模式和NAT模式的区别
  7. 高度为5的3阶b树含有的关键字个数_B-树和B+树的应用:数据搜索和数据库索引...
  8. caffe编译关于imread问题的解决
  9. pdf转word乱码怎么办,可能是你没用对工具
  10. VSCode + LaTeX 入门(学习记录)
  11. 微信统一服务(小程序服务通知与微信公众号模板消息)发送
  12. 英语时态8种基本时态讲解
  13. java.lang.NoClassDefFoundError: Could not initialize class com.cyj.util.Jdbc
  14. 【GCC】Linux GCC 常用命令和EFF文件格式
  15. JAVA面试八股文宝典(黑马学习随笔)-- 基础篇
  16. java获取经纬度_java调用高德地图api获取某个位置的经纬度
  17. string.h头文件的简单运用
  18. ③【Java 组】蓝桥杯省赛真题 [黄金连分数][马虎的算式]持续更新中...
  19. Creo 导入图片不显示
  20. 通过客户流失预测案例感悟数据分析设计方法思考——数据驱动、AI驱动

热门文章

  1. 当PowerDesigner的工具栏不见时候该怎么调出来
  2. Ubuntu18.04安装ROS Melodic(解决网络原因,先将所需压缩包下载到本地,然后rosdep update)
  3. linux脚本调用db2存储过程,LINUX定时执行含有DB2存储过程的SHELL脚本
  4. visual studio如何用低版本打开高版本项目
  5. 火星舱如何备份oracle_倒计时!火星,我们来了
  6. Android 使用Nginx rtmp 模块
  7. EXCEL怎么按照数字大小排列
  8. oracle的globalname后缀,在Oracle 11g下查看数据库的global_name
  9. java action提交表单数据,form表单action提交详解
  10. 天国近了(一) -- 揭穿OOP神话