2.6.1. Undo-log撤销日志
Undo即撤销的意思,通常也称为回滚日志,用来给MySQL撤销SQL操作的。
当一条写入类型的SQL执行时,都会记录Undo-log日志,Undo-log并不存在单独的日志文件,InnoDB默认是将Undo-log存储在xx.ibdata共享表数据文件当中,默认采用段的形式存储。
当一个事务尝试写某行表数据时,首先会将旧数据拷贝到xx.ibdata文件中,将表中行数据的隐藏字段:roll_ptr回滚指针指向xx.ibdata文件中的旧数据,然后再写表上的数据。需要回滚时,直接将roll_ptr回滚指针指向的Undo记录,从xx.ibdata共享表数据文件中拷贝到xx.ibd表数据文件,覆盖掉原本改动过的数据。
如果是insert操作,由于插入之前这条数据都不存在,就不会产生Undo记录,因此隐藏字段中的roll_ptr=null,直接用null覆盖插入的新记录即可,就实现了删除数据的效果。
在共享表数据文件中,有一块区域名为Rollback Segment回滚段,每个回滚段中有1024个Undo-log Segment,每个Undo段可存储一条旧数据,而执行写SQL时,Undo-log就是写入到这些段中。
MySQL5.5版本前,默认只有一个Rollback Segment,在MySQL5.5版本后,默认有128个回滚段,支持128*1024条Undo记录同时存在。
2.6.1.1. 基于Undo版本链实现MVCC
Undo-log中记录的旧数据并不仅仅只有一条,一条相同的行数据可能存在多条不同版本的Undo记录,内部会通过roll_ptr回滚指针,组成一个单向链表,而这个链表则被称之为Undo版本链。
2.6.1.2. Undo-log的内存缓冲区
InnoDB在MySQL启动时,会在内存中构建一个BufferPool,这个缓冲池主要存放一类是数据相关的缓冲,如索引、锁、表数据等,另一类则是各种日志的缓冲,如Undo、Bin、Redo....等日志。
一条写SQL执行时,不会直接往磁盘中的xx.ibdata文件写数据,会写在undo_log_buffer缓冲区中,因为工作线程直接去写磁盘太影响效率了,写进缓冲区后会由后台线程去刷写磁盘。
当一个事务提交时,Undo的旧记录不会立马删除Undo记录,对于旧记录的删除工作,InnoDB中会有专门的purger线程负责,purger线程内部会维护一个ReadView,它会以此作为判断依据,来决定何时移除Undo记录。
因为可能会有其他事务在通过快照,读Undo版本链中的旧数据,直接移除可能会导致其他事务读不到数据,因此删除的工作就交给了purger线程。
2.6.1.3. Undo-log相关的参数
在MySQL5.5之前没有太多参数,如:
- innodb_max_undo_log_size:本地磁盘文件中,Undo-log的最大值,默认1GB。
- innodb_rollback_segments:指定回滚段的数量,默认为1个。
其他参数基本上是在MySQL5.6才有的,如下:
- innodb_undo_directory:指定Undo-log的存放目录,默认放在.ibdata文件中。
- innodb_undo_logs:指定回滚段的数量,默认为128个,也就是之前的innodb_rollback_segments。
- innodb_undo_tablespaces:指定Undo-log分成几个文件来存储,必须开启innodb_undo_directory参数。
- innodb_undo_log_truncate:是否开启Undo-log的在线压缩功能,即日志文件超过大小一半时自动压缩,默认OFF关闭。
MySQL5.5版本以后,Undo-log日志支持单独存放,并且多出了几个参数可以调整Undo-log的区域。