MySQL-事务日志
1. 四种特性的实现机制
- 事务的隔离性由 锁机制 实现
- REDO LOG 称为 重做日志 ,提供再写入操作,恢复提交事务修改的页操作,用来保证事务的持久性
- UNDO LOG 称为 回滚日志 ,回滚行记录到某个特定版本,用来保证事务的原子性、一致性
2. redo log 和 undo log
- redo log:是存储引擎层(innodb)生成的日志,记录的是物理级别上的页修改操作,比如页号xxx、偏移量yyy写入了zzz数据。主要为了保证数据的可靠性
- undo log:是存储引擎层(innodb)生成的日志,记录的是逻辑操作日志,比如对某一行数据进行了INSERT语句操作,那么undo log就记录一条与之相反的DELETE操作。主要用于事务的回滚(undo log记录的是每个修改操作的逆操作)和一致性非锁定读(undo log回滚行记录到某种特定的版本--MVCC,即多版本并发控制)
redo日志
1. redo的作用
- InnoDB引擎的事务采用了WAL技术(Write-Ahead Logging),这种技术的思想就是先写日志,再写磁盘,只有日志写入成功,才算事务提交成功,这里的日志就是redo log
- 当发生宕机且数据未刷到磁盘的时候,可以通过redo log来恢复,保证ACID中的D
- Write-Ahead Log(预先日志持久化):在持久化一个数据页之前,先将内存中相应的日志页持久化
2. redo的好处
- redo日志降低了刷盘频率
- redo日志占用的空间非常小
3. redo的特点
- redo日志是顺序写入磁盘的
- 事务执行过程中,redo log不断记录
4. redo的组成
- 重做日志的缓冲 (redo log buffer) ,保存在内存中,有一大块内存空间被划分成若干个连续的redo log block。一个redo log block占用512字节大小
- redo log buffer 大小,默认 16M ,最大值是4096M,最小值为1M
# 查看大小
show variables like '%innodb_log_buffer_size%';
- 重做日志文件 (redo log file) ,保存在硬盘中,是持久的,用来恢复数据的文件
5. redo的整体流程
- 第1步:先将原始数据从磁盘中读入内存中来,修改数据的内存拷贝
- 第2步:生成一条重做日志并写入redo log buffer,记录的是数据被修改后的值
- 第3步:当事务commit时,将redo log buffer中的内容刷新到 redo log file,对 redo log file采用追加写的方式
- 第4步:定期将内存中修改的数据刷新到磁盘中
6. redo log的刷盘策略
- redo log的写入并不是直接写入磁盘的,InnoDB引擎会在写redo log的时候先写redo log buffer,之后以 一定的频率 刷入到真正的redo log file 中
- InnoDB给出 innodb_flush_log_at_trx_commit 参数,该参数控制 commit提交事务时,如何将 redo log buffer 中的日志刷新到 redo log file 中
- 设置为0 :表示每次事务提交时不进行刷盘操作。(系统默认master thread每隔1s进行一次重做日志的同步)
- 设置为1 :表示每次事务提交时都将进行同步,刷盘操作( 默认值 )
- 设置为2 :表示每次事务提交时都只把 redo log buffer 内容写入 page cache,不进行同步。由os自己决定什么时候同步到磁盘文件
- InnoDB存储引擎有一个后台线程,每隔1秒,就会把redo log buffer中的内容写到文件系统缓存(page cache),然后调用刷盘操作
- 还有一种情况,当redo 1og buffer占用的空间即将达到innodb_log_buffer_size(这个参数默认是16M)的一半的时候,后台线程会主动刷盘
7. innodb_flush_log_at_trx_commit 参数的取值效率
- 为0时,master thread中每1秒进行一次重做日志的fsync操作,因此实例crash最多丢失1秒钟内的事务。(master thread是负责将缓冲池中的数据异步刷新到磁盘,保证数据的一致性)它的IO效率理论是高于1的,低于2的,这种策略也有丢失数据的风险,也无法保证D持久化
- 为1时,只要事务提交成功,redo log记录就一定在硬盘里,不会有任何数据丢失。如果事务执行期间MySQL挂了或宕机,这部分日志丢了,但是事务并没有提交,所以日志丢了也不会有损失。可以保证ACID的D,数据绝对不会丢失,但是效率最差的
- 为2时,只要事务提交成功,redo log buffer中的内容只写入文件系统缓存(page cache)。
如果仅仅只是MySQL挂了不会有任何数据丢失,但是操作系统宕机可能会有1秒数据的丢失,这种情况下无法满足ACID中的D。但是数值2肯定是效率最高的
8. Mini-Transaction
- MySQL把对底层页面中的一次原子访问的过程称之为一个Mini-Transaction,简称mtr,比如,向某个索引对应的B+树中插入一条记录的过程就是一个Mini-Transaction
- 一个所谓的mtr可以包含一组redo日志,在进行崩溃恢复时这一组redo日志作为一个不可分割的整体
- 一个事务可以包含若干条语句,每一条语句其实是由若干个mtr组成,每一个mtr又可以包含若干条redo日志
9. redo 日志写入log buffer
- 向log buffer中写入redo日志的过程是顺序的,也就是先往前边的block中写,当该block的空闲空间用完之后再往下一个block中写
- InnoDB的设计者特意提供了一个称之为buf_free的全局变量,该变量指明后续写入的redo日志应该写入到log buffer中的哪个位置
10. redo log block的结构
- 一个redo log block是由日志头、日志体、日志尾组成
- 日志头占用12字节,日志尾占用8字节,所以一个block真正能存储的数据就是512-12-8=492字节
- 真正的redo日志都是存储到占用496字节大小的log block body中,log block header和log block trailer存储的是一些管理信息
redo log file
1. 参数 innodb_log_group_home_dir
- 指定 redo log 文件组所在的路径,默认值为 ./ ,表示在数据库的数据目录下
- MySQL的默认数据目录( var/lib/mysql )下默认有两个名为 ib_logfile0 和ib_logfile1 的文件,log buffer中的日志默认情况下就是刷新到这两个磁盘文件中
- 可以修改此参数来设置redo日志文件位置
2. 参数 innodb_log_files_in_group
- 指明redo log file的个数,默认2个,最大100个
- 命名方式如:ib_logfile0,ib_logfile1...ib_logfilen
3. 参数 innodb_flush_log_at_trx_commit
- 控制 redo log 刷新到磁盘的策略,默认为1
4. 参数 innodb_log_file_size
- 单个 redo log 文件设置大小,默认值为 48M 。最大值为512G
- 注意最大值指的是整个 redo log 系列文件之和,即(innodb_log_files_in_group * innodb_log_file_size )不能大于最大值512G
# my.cnf文件,永久生效
innodb_log_file_size=200M
5. 日志文件组
- 循环的方式向redo日志文件组里写数据
6. checkpoint
- write pos是当前记录的位置,一边写一边后移
- checkpoint 是当前要擦除的位置,也是往后推移
- 每次刷盘 redo log记录到日志文件组中,write pos位置就会后移更新
- 每次MySQL加载日志文件组恢复数据时,会清空加载过的redo log记录,并把checkpoint后移更新
- write pos和checkpoint之间的还空着的部分可以用来写入新的redo log记录
- 如果 write pos 追上 checkpoint ,表示日志文件组满了,这时候不能再写入新的 redo log记录,MySQL 得停下来,清空一些记录,把 checkpoint 推进一下
Undo日志
1. Undo日志介绍
- undo log是事务原子性的保证。在事务中 更新数据 的 前置操作 其实是要先写入一个 undo log
- MySQL把为了回滚而记录的这些内容称之为撤销日志或者回滚日志(即undo log)
- undo log会产生redo log,也就是undo log的产生会伴随着redo log的产生,这是因为undo log也需要持久性的保护
2. Undo日志的作用
- 回滚数据:将数据库逻辑地恢复到原来的样子
- MVCC:当用户读取一行记录时,若该记录已经被其他事务占用,当前事务可以通过undo读取之前的行版本信息,以此实现非锁定读取
3. 回滚段
- InnoDB对undo log的管理采用段的方式,也就是 回滚段(rollback segment)
- 每个回滚段记录了1024 个 undo log segment ,而在每个undo log segment段中进行 undo页 的申请
- 在 InnoDB1.1版本之前 (不包括1.1版本),只有一个rollback segment,因此支持同时在线的事务限制为 1024 。虽然对绝大多数的应用来说都已经够用
- 从1.1版本开始InnoDB支持最大 128个rollback segment ,故其支持同时在线的事务限制提高到了 128*1024
show variables like 'innodb_undo_logs';
4. 对回滚段的设置
- innodb_undo_directory:设置rollback segment文件所在的路径。这意味着rollback segment可以存放在共享表空间以外的位置,即可以设置为独立表空间。该参数的默认值为./”,表示当前InnoDB存储引擎的目录
- innodb_undo_logs:设置rollback segment的个数,默认值为128。在InnoDB1.2版本中,该参数用来替换之前版本的参数innodb_rollback_segments
- innodb_undo_tablespaces:设置构成rollback segment文件的数量,这样rollback segment可以较为平均地分布在多个文件中。设置该参数后,会在路径innodb_undo_directory看到undo为前缀的文件,该文件就代表rollback segment文件
5. Undo页的重用
- 当事务提交时,并不会立刻删除undo页。因为重用,所以这个undo页可能混杂着其他事务的undo log
- undo log在commit后,会被放到一个链表中,然后判断undo页的使用空间是否小于3/4,如果小于3/4的话,则表示当前的undo页可以被重用,那么它就不会被回收,其他事务的undo log可以记录在当前undo页的后面
- 由于undo log是离散的,所以清理对应的磁盘空间时,效率不高
6. 回滚段与事务
- 每个事务只会使用一个回滚段,一个回滚段在同一时刻可能会服务于多个事务
- 当一个事务开始的时候,会制定一个回滚段,在事务进行的过程中,当数据被修改时,原始的数据会被复制到回滚段
- 在回滚段中,事务会不断填充盘区,直到事务结束或所有的空间被用完。如果当前的盘区不够
用,事务会在段中请求扩展下一个盘区,如果所有已分配的盘区都被用完,事务会覆盖最初的盘区或者在回滚段允许的情况下扩展新的盘区来使用 - 回滚段存在于undo表空间中,在数据库中可以存在多个undo表空间,但同一时刻只能使用一个undo表空间
- 当事务提交时,将undo log放入列表中,以供之后的purge操作。判断undo log所在的页是否可以重用,若可以分配给下个事务使用
7. 回滚段中的数据分类
- 未提交的回滚数据(uncommitted undo information):该数据所关联的事务并未提交,用于实现读一致性,所以该数据不能被其他事务的数据覆盖
- 已经提交但未过期的回滚数据(committed undo information):该数据关联的事务已经提交,但是仍受到undo retention参数的保持时间的影响
- 事务已经提交并过期的数据(expired undo information):事务已经提交,而且数据保存时间已经超过undo retention参数指定的时间,属于已经过期的数据。当回滚段满了之后,会优先覆盖“事务已经提交并过期的数据"
8. undo log的删除
- 事务提交后并不能马上删除undo log及undo log所在的页。这是因为可能还有其他事务需要通过undo log来得到行记录之前的版本
- 事务提交时将undo log放入一个链表中,是否可以最终删除undo log及undo log所在页由purge线程来判断
9. 在InnoDB存储引擎中,undo log分为:
- insert undo log是指在insert操作中产生的undo log。因为insert操作的记录,只对事务本身可见,对其他事务不可见(这是事务隔离性的要求),故该undo log可以在事务提交后直接删除。不需要进行purge操作
- update undo log记录的是对delete和update操作产生的undo log。该undo log可能需要提供MVCC机制,因此不能在事务提交时就进行删除。提交时放入undo log链表,等待purge线程进行最后的删除
10. purge线程的主要作用
- 清理undo页和清除page里面带有Delete_Bit标识的数据行
- 在lnnoDB中,事务中的Delete操作实际上并不是真正的删除掉数据行,而是一种Delete Mark操作,在记录上标识Delete_Bit,而不删除记录。是一种“假删除,只是做了个标记,真正的删除工作需要后台purge线程去完成
undo log生命周期
1. 只有Buffer Pool的执行流程
2. 有Redo Log和Undo Log 的流程