MySQL 事务日志

事务特性实现机制

1、隔离性由锁机制实现

2、原子性、一致性、持久性由事务的 redo 日志、undo 日志来保证

(1)REDO LOG:重做日志,提供再写入操作,恢复提交事务修改的页操作,用来保证事务的持久性

(2)UNDO LOG:回滚日志,回滚行记录到某个特定版本,用来保证事务的原子性、一致性

3、UNDO 不是 REDO 逆过程,REDO 和 UNDO 都可以视为是一种恢复操作

(1)redo log:是存储引擎层(InnoDB)生成的日志,记录物理级别上的页修改操作,保证数据的可靠性

(2)undo log:是存储引擎层(InnoDB)生成的日志,记录逻辑操作日志,用于事务回滚,记录每个修改操作的逆操作和一致性非锁定读,undo log 回滚行记录到某种特定的版本,即 MVCC(多版本并发控制)

 

redo 日志

1、没有 redo 日志难以保证持久性

(1)InnoDB 以页为单位管理存储空间

(2)在真正访问页面之前,需要把在磁盘上的页,缓存到内存中的 Buffer Pool 之后才可以访问

(3)所有变更都必须先更新缓冲池中的数据,然后缓冲池中的脏页会以一定的频率被刷入磁盘(checkpoint 机制)

(4)通过缓冲池优化 CPU 和磁盘,可以保证整体的性能不会下降太快

(5)由于 checkpoint 并不是每次变更时就触发,而是 master 线程隔一段时间去处理,若事务提交后,刚写完缓冲池,数据库宕机,则丢失这段数据,无法恢复

2、保证持久性方式一

(1)在事务提交完成之前,把该事务所修改的所有页面都刷新到磁盘

(2)修改量与刷新磁盘工作量严重不成比例,有时仅修改某个页面中的一个字节,不得不将一个完整的页面(16KB)从内存中刷新到磁盘

(3)随机 I/O 刷新较慢:一个事务可能包含很多语句,即使是一条语句也可能修改许多页面,假如该事务修改的页面可能并不相邻,将某个事务修改 Buffer Pool 中的页面刷新到磁盘时,需要进行很多随机 I/O

3、保证持久性方式二:只需要记录修改

(1)InnoDB 事务采用预先日志持久化(Write-Ahead Logging)

(2)先写日志,再写磁盘,只有日志写入成功,才算事务提交成功,日志就是 redo log

(3)当发生宕机且数据未刷到磁盘时,可以通过 redo log 来恢复

image-20220607141438474

4、好处

(1)降低刷盘频率

(2)日志占用的空间非常小

(3)存储表空间 ID、页号、偏移量、需要更新的值,所需的存储空间小,刷盘快

5、特点

(1)redo 日志是顺序写入磁盘

(2)在执行事务的过程中,每执行一条语句,可能产生若干条 redo 日志,使用顺序 l/O,效率比随机 I/O 快

(3)事务执行过程中,redo log 不断记录

6、redo log、bin log 区别

(1)redo log 由存储引擎层产生;bin log 由数据库层产生

(2)一个事务过程中,一直不断的往 redo log 顺序记录;而 bin log 不会记录,直到这个事务提交,才会一次写入到 bin log 文件中

7、组成 

(1)重做日志缓冲(redo log buffer):保存在内存中,是易失的,在服务器启动时,向操作系统申请,

(2)redo log buffer 被划分成若干个连续的重做日志文件,一个 redo log block 占用 512 字节

(3)redo log buffer 默认大小 16M,最大值 4096M,最小值 1M

(4)查看 redo log buffer 大小

SHOW VARIABLES LIKE '%innodb_log_buffer_size%';

(5)重做日志文件(redo log file):保存在硬盘中,是持久的

(6)默认在数据库的根路径下,其中 ib_logfile8、ib_logfile1即为重做日志文件

8、redo 整体流程

(1)先将原始数据从磁盘中读入内存中来,修改数据的内存拷贝

(2)生成一条重做日志,并写入 redo log buffer,记录数据被修改后的值

(3)当事务提交时,将 redo log buffer 中的内容刷新到 redo log file,对 redo log file 采用追加写的方式

(4)定期将内存中修改的数据刷新到磁盘中

(5)Write-Ahead Log(预先日志持久化):在持久化一个数据页之前,先将内存中相应的日志页持久化

image-20220607141642235

 

9、redo log 刷盘策略

(1)redo log 不是直接写入磁盘,InnoDB 会在写 redo log 时,先写 redo log buffer,再以一定的频率刷入到真正 redo log file 中

(2)redo log buffer 刷盘到 redo log file 过程,不是真正的刷到磁盘中去,只是刷入到文件系统缓存(page cache)中,是现代操作系统为了提高文件写入效率做的一个优化,真正的写由系统决定(比如 page cache 足够大)

(3)对于 InnoDB,如果由系统来同步,同样如果系统宕机,那么数据也丢失

(4)InnoDB 给出 innodb_flush_log_at_trx_commit 参数,该参数控制提交事务时,如何将 redo log buffer 中的日志刷新到 redo log file 中,支持三种策略

(5)设置为 0:表示每次事务提交时,不进行刷盘操作,系统默认 master thread 每隔 1s 进行一次重做日志的同步

(6)设置为 1(默认):表示每次事务提交时都将进行同步,刷盘操作

(7)设置为 2:表示每次事务提交时都只把 redo log buffer 内容写入 page cache,不进行同步,由 OS 决定同步到磁盘文件时间

(8)InnoDB 有一个后台线程,每隔 1 秒,就把 redo log buffer 中的内容写到文件系统缓存(page cache),然后调用刷盘操作,即一个没有提交事务的 redo log 记录,也可能被后台线程刷盘

image-20220607233216957

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

 

写入 redo log buffer 过程

1、Mini-Transaction

(1)mtr:MySQL 把对底层页面中的一次原子访问的过程

(2)一个 mtr 可以包含一组 redo 日志,在进行崩溃恢复时,这一组 redo 日志作为一个不可分割的整体

(3)一个事务可以包含若干条语句,每一条语句由若干个 mtr 组成,每一个 mtr 可以包含若干条 redo 日志

image-20220607142816249

2、redo 日志写入 log buffer

(1)向 log buffer 中写入 redo 日志的过程是顺序,即先往前边 block 中写,当该 block 空闲空间用完之后,再往下一个 block 中写

(2)选择 block 的偏移量处:InnoDB 提供一个 buf_free 全局变量,该变量指明后续写入 redo 日志应该写入到 log buffer 哪个位置

image-20220608094545215

(3)redo 日志是一个不可分割的组,并不是每生成一条 redo 日志,就将其插入到 log buffer 中,而是每个 mtr 运行过程中,产生的日志先暂时存到一个地方,当该 mtr 结束时,将过程中产生的一组 redo 日志,再全部复制到 log buffer 中

(4)不同的事务可能是并发执行,每当一个 mtr 执行完成时,伴随该 mtr 生成的一组 redo 日志,就需要被复制到 log buffer 中,不同事务 mtr 可能是交替写入 log buffer

image-20220607143121663

3、redo log block 结构图

(1)组成:日志头、日志体、日志尾,总共 512 字节

(2)日志头占用 12 字节,日志尾占用 4 字节,日志体最大占用 512-12-4=496 字节

(3)512 字节与磁盘的扇区有关,机械磁盘默认扇区 512 字节,若写入的数据大于 512 字节,则写入的扇区不止一个,涉及到盘片转动,找到下一个扇区

(4)假设现在需要写入两个扇区 A、B,如果扇区 A 写入成功,而扇区 B 写入失败,则出现非原子性写入,而如果每次只写入和扇区的大小一样的 512 字节,那么每次的写入都是原子性的

image-20220607143207628

(5)log block header、log block trailer 存储管理信息

image-20220607143215221

4、log block header

(1)LOG_BLOCK_HDR_NO:log buffer 由 log block 组成,log buffer 看作一个数组,LOG_BLOCK_HDR_NO 标记这个数组中的位置,其是递增并且循环使用的,占用 4 个字节,但是由于第—位用来判断是否是flush bit,所以最大的值为 2G。

(2)LOG_BLOCK_HDR_DATA_LEN:表示 block 中已经使用多少字节,初始值为 12(因为 log block body 从第 12 个字节处开始),如果 logblock body 已经被全部写满,则值被设置为 512

(3)LOG_BLOCK_FIRST_REC_GROUP:一条 redo 日志可称为一条 redo 日志记录(redo log record),一个 mtr 生产多条 redo 日志记录,这些 redo 日志记录被称为一个 redo 日志记录组(redo log record group);该值代表该 block 中第一个 mtr 生成 redo 日志记录组的偏移量,这个 block 中的第一个 mtr 生成的第一条 redo 日志的偏移量;如果该值的大小和 LOG_BLOCK_HDR_DATA_LEN 相同,表示当前 log block 不包含新的日志

(4)LOG_BLOCK_CHECKPOINT_NO:占用 4 字节,表示该 log block 最后被写入时的 checkpoint

5、log block trailer

(1)LOG_BLOCK_CHECKSUM:表示 block 校验值,用于正确性校验

(2)其值和 LOG_BLOCK_HDR_NO 相同

 

redo log file

1、相关参数设置

(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 个数,命名方式如:ib_logfile0,ib_logfile1... ib_logfilen,默认 2 个,最大 100 个

(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

(5)在数据库实例更新比较频繁的情况下,可以适当加大 redo log 组数和大小,不推荐 redo log 设置过大,在 MySQL 崩溃恢复时,会重新执行 REDO 日志中的记录

2、日志文件组

(1)磁盘上的 redo 日志文件不只一个,而是以一个日志文件组的形式出现的

(2)这些文件以 ib_logfile[数字](数字可以是 0、1、2……)形式进行命名,每个 redo 日志文件大小都是一样的,在将 redo 日志写入日志文件组时,是从 ib_logfile0 开始写,直到最后一个文件写满,重新转到 ib_logfile0 继续写

image-20220607143532553

(3)总共 redo 日志文件大小:innodb_log_file_size * innodb_log_files_in_group

(4)采用循环使用的方式向 redo 日志文件组里写数据,导致后写入 redo 日志覆盖掉前边写的 redo 日志,InnoDB 提出 checkpoint 概念

3、刷盘 redo log 记录到日志文件组

(1)write pos:当前记录的位置,一边写一边后移

(2)checkpoint:当前要擦除的位置,也是往后推移

(3)每次刷盘 redo log 记录到日志文件组中,write pos 位置就会后移更新,每次 MySQL 加载日志文件组恢复数据时,清空加载的 redo log 记录,并把 checkpoint 后移更新

(4)write pos、checkpoint 之间空部分可以用来写入新 redo log 记录

image-20220607143642924

(5)如果 write pos 追上 checkpoint ,表示日志文件组已满,这时不能再写入新 redo log 记录,MySQL 需要清空一些记录,推进 checkpoint

image-20220607143714885

 

Undo 日志

1、在事务中更新数据的前置操作,要先写入一个 undo log

2、INSERT、DELETE、UPDATE 都会产生 undo log,由于 SELECT 不会修改任何用户记录,所以在查询操作执行时,并不需要记录相应 undo 日志

3、undo log 产生会伴随着 redo log 产生,因为 undo log 需要持久性的保护

4、作用

(1)回滚数据:undo 是逻辑日志,所有修改都被逻辑地取消,但是数据结构和页本身在回滚之后可能不相同,因为在多用户并发系统中,可能存在多个并发事务,数据库的主要任务就是协调对数据记录的并发访问,因此,不能将一个页回滚到事务开始时,因为会影响其他事务正在进行的工作

(2)MVCC:在 InnoDB 中 MVCC 通过 undo 实现,当用户读取一行记录时,若该记录已经被其他事务占用,当前事务可以通过 undo 读取之前的行版本信息,以此实现非锁定读取

5、存储结构:回滚段、undo 页

(1)InnoDB 采用段的方式管理 undo log,即回滚段(rollback segment)

(2)每个回滚段记录 1024 个 undo log segment,每个 undo log segment 段中进行 undo 页的申请

(3)在 InnoDB 1.1 版本之前 (不包括 1.1),只有一个 rollback segment,因此支持同时在线的事务限制为 1024

(4)从 1.1 版本开始,InnoDB 支持最大 128 个 rollback segment ,支持同时在线的事务限制提高到 128 * 1024,但 rollback segment 都存储于共享表空间 ibdata 中

(5)查看 rollback segment 个数

SHOW VARIABLES LIKE 'innodb_undo_logs';

(6)从 InnoDB 1.2 版本开始,可通过参数对 rollback segment 做进一步的设置

(7)innodb_undo_direclory:设置 rollback segment 文件所在的路径,rollback segment 可以存放在共享表空间以外的位置,即可以设置为独立表空间,该参数的默认值为 .,表示当前 InnoDB 存储引擎的目录

(8)innodb_undo_logs:设置 rollback segment 个数,默认值为 128,在 InnoDB 1.2 版本中,该参数用来替换之前版本的参数 innodb_rollback_segments

(9)innodb_undo_tablespaces:设置构成 rollback segment 文件数量(undo 表空间个数),rollback segment 可以较为平均地分布在多个文件中,设置该参数后,会在路径 innodb_undo_directory 看到 undo 为前缀的文件,该文件代表 rollback segment 文件,默认值为 0,表示不独立设置 undo 表空间,默认记录到 ibdata 中

(10)undo log相关参数一般很少改动

6、undo 页的重用

(1)当开启一个事务需要写 undo log 时,先在 undo log segment 中去找到一个空闲位置,当有空位时,就申请 undo 页,在申请到的 undo 页中进行 undo log 写入

(2)mysql 默认一页大小 16K,为每一个事务分配一个页,磁盘空间增长的非常快,而且浪费很多空间

(3)重用:当事务提交时,并不会立刻删除 undo 页,该 undo 页可能包含其他事务 undo log,undo log 在 commit 后,会被放到一个链表中,然后判断 undo 页的使用空间是否小于 3/4,如果小于 3/4 的话,则表示当前 undo 页可以被重用,那么它就不会被回收,其他事务 undo log 可以记录在当前 undo 页后

(4)由于 undo log 是离散的,所以清理对应的磁盘空间时,效率不高

7、回滚段与事务

(1)每个事务只会使用一个回滚段(rollback segment),一个回滚段在同一时刻可能会服务于多个事务

(2)当一个事务开始时,会制定一个回滚段,在事务进行的过程中,当数据被修改时,原始数据会被复制到回滚段

(3)在回滚段中,事务会不断填充盘区,直到事务结束或所有的空间被用完,如果当前盘区不够用,事务会在段中请求扩展下一个盘区,如果所有已分配的盘区都被用完,事务会覆盖最初的盘区,或在回滚段允许的情况下,扩展新的盘区来使用

(4)回滚段存在于 undo 表空间中,在数据库中可以存在多个 undo 表空间,但同一时刻只能使用一个 undo 表空间

(3)当事务提交时,InnoDB 会将 undo log 放入列表中,以供之后 purge 操作,判断 undo log 所在页是否可以重用,若可以分配给下个事务使用

8、回滚段中的数据分类

(1)未提交的回滚数据(uncommitted undo information):该数据所关联的事务并未提交,用于实现读一致性,所以该数据不能被其他事务的数据覆盖

(2)已经提交但未过期的回滚数据(committed undo information):该数据关联的事务已经提交,但是仍受到 undo retention 参数的保持时间的影响

(3)事务已经提交并过期的数据(expired undo information):事务已经提交,而且数据保存时间超过 undo retention 参数指定的时间,当回滚段满了之后,会优先覆盖事务已经提交并过期的数据

(4)事务提交后并不能马上删除 undo log 及 undo log 所在的页,因为可能还有其他事务需要通过 undo log 来得到行记录之前的版本,所以事务提交时,将 undo log 放入一个链表中,是否可以删除 undo log 及 undo log 所在页由 purge 线程判断

9、InnoDB 的 undo 类型

(1)insert undo log:在 INSERT 中产生的 undo log,只对事务本身可见,对其他事务不可见,所以 undo log 可以在事务提交后直接删除,不需要进行 purge 操作

(2)update undo log:记录 DELETE、UPDATE 产生的 undo log,该 undo log 可能需要提供 MVCC 机制,因此不能在事务提交时就进行删除,提交时放入 undo log 链表,等待 purge 线程进行最后的删除

10、undo log 简要生成过程

11、InnoDB 每个行记录除了记录本身的数据之外,还有几个隐藏列

(1)DB_ROW_ID:如果没有为表显式的定义主键,并且表中也没有定义唯一索引,则 InnoDB 自动为表添加一个 row_id 隐藏列作为主键

(2)DB_TRX_ID:每个事务都会分配一个事务 ID,当对某条记录发生变更时,就会将这个事务的事务 ID 写入 trx_id 中

(3)DB_ROLL_PTR:回滚指针,本质是指向 undo log 指针

12、回滚过程

(1)每次变更数据都会产生一个 undo log,且 undo log 序号递增,当一条记录被变更多次时,则产生多条 undo log

(2)undo log 记录的是变更前的日志,并且每个 undo log 序号是递增的,当要回滚时,按照序号依次向前推,就可以找到原始数据

posted @   半条咸鱼  阅读(268)  评论(0编辑  收藏  举报
(评论功能已被禁用)
相关博文:
阅读排行:
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 实操Deepseek接入个人知识库
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
点击右上角即可分享
微信分享提示