前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >mysql事务-redo/undo log

mysql事务-redo/undo log

作者头像
leobhao
发布2022-06-28 18:38:59
6110
发布2022-06-28 18:38:59
举报
文章被收录于专栏:涓流涓流

事务的持久性(Redo Log)

我们操作 Mysql 数据的时候,都是把数据页加载到 Buffer Pool 中才可以访问,但是事务是需要具有持久性的,如果我们只再内存的Buffer Pool中修改了页面,假设在事务提交后突然发生了某个故障,导致内存中的数据都失效了,那么这个已经提交了的事务对数据库中所做的更改也就跟着丢失了。为了保证事务的持久性,我们需要在事务提交完成之前把该事务所修改的所有页面都刷新到磁盘。

但是会面临一些问题:

  • 刷新一个完整的数据页太浪费了:有时候我们仅仅修改了某个页面中的一个字节,但是我们知道在InnoDB中是以页为单位来进行磁盘IO的,也就是说我们在该事务提交时不得不将一个完整的页面从内存中刷新到磁盘,我们又知道一个页面默认是16KB大小,只修改一个字节就要刷新16KB的数据到磁盘上显然是太浪费了
  • 随机IO刷起来比较慢: 一个事务可能包含很多语句,即使是一条语句也可能修改许多页面,但是该事务修改的这些页面可能并不相邻,这就意味着在将某个事务修改的Buffer Pool中的页面刷新到磁盘时,需要进行很多的随机IO,随机IO比顺序IO要慢很多(page cache 的缘故)

我们只是想让已经提交了的事务对数据库中数据所做的修改永久生效,即使后来系统崩溃,在重启后也能把这种修改恢复出来。所以我们其实没有必要在每次事务提交时就把该事务在内存中修改过的全部页面刷新到磁盘,只需要把修改了哪些东西记录一下就好。 当系统崩溃后,重启只需要按照记录的内容更新数据页,那么事务对数据库做的修改又可以被恢复出来。这种被记录的内容被称为redo log(重做日志)。使用 redo log 的优点:

  • redo log 占用空间很小
  • redo log 是顺序写入的(顺序IO)
redo log 缓冲区

InnoDB为了解决磁盘速度过慢的问题而引入了Buffer Pool。 同理,写入redo日志时也不能直接直接写到磁盘上,实际上在服务器启动时就向操作系统申请了一大片称之为redo log buffer的连续内存空间。 我们可以通过启动参数innodb_log_buffer_size来指定log buffer的大小,在MySQL 5.7.21这个版本中,该启动参数的默认值为16MB

redo log 刷盘时机

事务运行过程中产生的一组redo日志在mtr结束时会被复制到 log buffer 中,这些日志页不会一直存在内存中,在一些情况下它们会被刷新到磁盘里,比如:

  • log buffer 空间不足时: 如果当前写入 log buffer 的redo日志量已经占满了 log buffer 总容量的大约一半左右,就需要把这些日志刷新到磁盘上。
  • 事务提交时: 在事务提交时可以不把修改过的Buffer Pool页面刷新到磁盘,但是为了保证持久性,必须要把修改这些页面对应的redo log刷新到磁盘。
  • 后台线程刷新: 后台有一个线程,大约每秒都会刷新一次log buffer中的 redo log 到磁盘。
  • checkpoint
checkpoint

redo log 只是为了系统崩溃后恢复脏页用的,如果对应的脏页已经刷新到了磁盘(flush 链表中的更改已经落到了磁盘上),也就是说即使现在系统崩溃,那么在重启后也用不着使用redo日志恢复该页面了,所以该redo 日志也就没有存在的必要了,那么它占用的磁盘空间就可以被后续的redo日志所重用。也就是说:判断某些redo日志占用的磁盘空间是否可以覆盖的依据就是它对应的脏页是否已经刷新到磁盘里。

Innodb 有个一全局变量checkpoint_lsn(checkpoint log sequence number)来代表当前系统中可以被覆盖的redo日志总量是多少。假如某个页a被刷新到了磁盘,mtr_1生成的redo日志就可以被覆盖了,所以我们可以进行一个增加checkpoint_lsn的操作,我们把这个过程称之为做一次checkpoint。

checkpoint 操作也会释放 log buffer 的空间,因为 log buffer 的容量是有限的,所以 Innodb 采用循环记录的方式。check point 会释放出已经刷新到磁盘的 redo log, 释放出空间。不用去细想checkpoint是怎么操作的,只需要知道经过checkpoint后,log buffer 会释放出已经刷新到磁盘的 redo日志空间

innodb_flush_log_at_trx_commit

前边说为了保证事务的持久性,用户线程在事务提交时需要将该事务执行过程中产生的所有redo日志都刷新到磁盘上。这一条要求太狠了,会很明显的降低数据库性能。如果有的同学对事务的持久性要求不是那么强烈的话,可以选择修改一个称为innodb_flush_log_at_trx_commit的系统变量的值,该变量有3个可选的值:

  • 0:当该系统变量值为0时,表示在事务提交时不立即向磁盘中同步redo日志,这个任务是交给后台线程做的。 这样很明显会加快请求处理速度,但是如果事务提交后服务器挂了,后台线程没有及时将redo日志刷新到磁盘,那么该事务对页面的修改会丢失。这种操作会增加吞吐量,但是对数据要求很严格的场景,数据库 crash 会导致数据丢失
  • 1:当该系统变量值为1时,表示在事务提交时需要将redo日志同步到磁盘,可以保证事务的持久性。1也是innodb_flush_log_at_trx_commit的默认值。
  • 2:当该系统变量值为2时,表示在事务提交时需要将redo日志写到操作系统的缓冲区中,但并不需要保证将日志真正的刷新到磁盘。由操作系统来决定什么时候刷盘(操作系统的 page cache 刷盘策略), 这种情况下如果数据库挂了,操作系统没挂的话,事务的持久性还是可以保证的,但是操作系统也挂了的话,那就不能保证持久性了

事务回滚的记录(Undo Log)

事务需要保证原子性(要么全部成功要么全部失败),所以当事务执行到一半出现错误或程序手动指定ROLLBACK语句时,需要将已执行的修改恢复(回滚),这样看起来这个事务就什么都没做,符合原子性的要求。

要满足事务的回滚,当对一条记录做改动时(这里的改动可以指INSERT、DELETE、UPDATE),都需要把回滚时所需的东西都给记下来。比如:

  • 插入一条记录时,至少要把这条记录的主键值记下来,之后回滚的时候只需要把这个主键值对应的记录删掉就好了。
  • 删除了一条记录,至少要把这条记录中的内容都记下来,这样之后回滚时再把由这些内容组成的记录插入到表中就好了。
  • 修改了一条记录,至少要把修改这条记录前的旧值都记录下来,这样之后回滚时再把这条记录更新为旧值就好了。

这些为了回滚而记录的称之为撤销日志(undo log)。这里需要注意的是select操作不会产生任何更改,所以并不需要记录对应的 redo log

事务id

当开启事务的时候,innodb 会为当前事务分配一个唯一的事务id, 本质上就是一个数字,它的分配策略和行记录的隐藏列row_id(当用户没有为表创建主键和UNIQUE键时InnoDB自动创建的列)的分配策略大抵相同,具体策略如下:

  • 服务器会在内存中维护一个全局变量,每当需要为某个事务分配一个事务id时,就会把该变量的值当作事务id分配给该事务,并且把该变量自增1
  • 每当这个变量的值为256的倍数时,就会将该变量的值刷新到系统表空间的页号为5的页面中一个称之为Max Trx ID的属性处,这个属性占用8个字节的存储空间
  • 当系统下一次重新启动时,会将上边提到的Max Trx ID属性加载到内存中,将该值加上256之后赋值给我们前边提到的全局变量(因为在上次关机时该全局变量的值可能大于Max Trx ID属性值), 这也是保证事务id在重启后不重复的一个方案

InnoDB记录行格式的时候:聚簇索引的记录除了会保存完整的用户数据以外,而且还会自动添加名为trx_id、roll_pointer(roll_pointer指向的是 记录所对应的 undo log)的隐藏列,如果用户没有在表中定义主键以及UNIQUE键,还会自动添加一个名为row_id的隐藏列。所以一条记录在页面中的真实结构看起来就是这样的:

其中 roll_pointer 是指向记录对应的 undo log 的一个指针

insert undo log

虽然 insert 操作实际上可能向聚簇索引和所有二级索引都插入一条记录, 但是记录 undo log 只需要记录聚簇索引都情况即可, 因为聚簇索引和二级索引是一一对应的, 在回滚插入操作的时候, 知道了主键信息就可以根据主键把所有二级索引删除

在 insert undo log 中就只记录了表id、主键长度、主键值

delete undo log
  1. 正常记录会在页内组成一个单向链表, 被删除记录也会形成一个单向链表(被删除记录 delete_mask 标志位为1), 在页头会有个 PAGE_FREE 的指针指向被删除记录链表的头节点
  1. 在进行 delete 操作的时候, 首先将正常记录链表对应记录的 delete_mask 置为 1(此时事务还没提交)
  2. 这个时候会进行一个 delete undo log 的记录, 如果发生回滚就只对上一步进行回滚
  3. 将被删除记录从正常链表移动到垃圾链表中(备注: 当一个页被回收足够多的垃圾链表, 默认 50% 阈值会发生页合并)
update undo log

Redo, Undo 在整个执行过程中的流程

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019-09-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 事务的持久性(Redo Log)
    • redo log 缓冲区
      • redo log 刷盘时机
        • checkpoint
          • innodb_flush_log_at_trx_commit
          • 事务回滚的记录(Undo Log)
            • 事务id
              • insert undo log
                • delete undo log
                  • update undo log
                  • Redo, Undo 在整个执行过程中的流程
                  相关产品与服务
                  云数据库 SQL Server
                  腾讯云数据库 SQL Server (TencentDB for SQL Server)是业界最常用的商用数据库之一,对基于 Windows 架构的应用程序具有完美的支持。TencentDB for SQL Server 拥有微软正版授权,可持续为用户提供最新的功能,避免未授权使用软件的风险。具有即开即用、稳定可靠、安全运行、弹性扩缩等特点。
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档