前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >重用的undo日志 (3)—mysql进阶(六十六)

重用的undo日志 (3)—mysql进阶(六十六)

作者头像
用户9919783
发布2022-07-29 13:49:12
3670
发布2022-07-29 13:49:12
举报
文章被收录于专栏:后端从入门到精通

前面说了undo日志的文件格式,第一页和后面的页是不同的,填入undo日志之前,会先把undo_page_header属性填满,还有undo_segment_header,undo_log_header。List base node存在undo segment header,list node存在每个undo 页面的undo_page_header。

不重用的undo日志 (2)—mysql进阶(六十五)

重用undo页面

前面说了为了提高并发性能,多个事务写入undo日志性能,innoDB每个事务都有单独的undo链表。但这样也会造成内存浪费,针对如果某个事务只执行了很少量的sql,这些undo日志只占用一点点空间,都占用一个事务的链表,是不是太浪费了,于是在事务提交后的某些情况下,重用该事务的undo页面链表。一个链表判断是否可以被重用非常简单:

该链表只包含一个undo页面

如果一个事务执行过程中产生很多的undo日志,那么可能有非常多的页面加入到undo页面链表中。在该事务提交后,如果将整个链表的页面都重用,意味着即是新的事务并没有向undo页面链表写入很多undo日志,那么该链表也得维护非常多的页面,那些用不到的页面也不能被别的事务使用,这样就造成一种浪费。所以innoDB规定,只有在undo链表包含一个undo页面时,该链表才可以被下一个事务使用。

该undo页面已经使用的空间小于整个页面空间的3/4。

Insert undo 和update undo重用的策略也是不同的。

Insert undo链表:

Insert只存储trx_undo_insert_rec的undo日志,这种类型在事务提交后就没用了,就可以清楚掉。所以在某个事务提交后,重用这个事务insert undo链表的时候,可以直接把之前事务写入一组undo日志覆盖掉,从头开始写。

假如有一个事务使用insert undo链表,到事务提交时,只想insert undo链表插入3条undo日志,这个insert undo链表只申请了一个undo页面。假设此刻该页面使用空间小于整个空间的3/4,那么下一个事务就可以重用这个insert undo链表(并且链表满足只有一个页面)。假设此刻有一个新事物重用了该insert undo链表,那么把之前的覆盖掉。

Update undo链表:

Insert undo链表在事务提交后,undo日志可以直接删除,但是update 日志不能再事务提交后直接删除(下一章mvcc会介绍为什么不能被删除)。所以后面的如果想重用,就不是覆盖,而是新加一条数据。

回滚段

我们知道一个事务执行最多分配4个undo页面链表。在同一时刻,不同事务拥有的undo页面链表是不同的,为了管理好这些链表,innoDB设计了rollback segment header页面,在这个页面存放各种链表的第一个页面first undo page的页号,这些页号称为undo slot。可以理解first undo page就是map的key,通过key能找到对应的value,value就是normal undo page。Key会统一管理在rollback segment header里面。

Rollback segment header结构一共16kb:

File header:38个字节。

Trx_rseg_max_Size:4个字节,本rollback segment里管理所有undo链表中undo页面数量之和最大值。换句话说,这里面页面之和不可以超过这个最大值,基本可以默认为无限大。

Trx_rseg_history_Size:4个字节,history链表占用的大小。

Trx_rseg_history:16个字节,history链表的基节点。

Trx_rseg_fseg_header:10个字节,本rollback segment对应10个字节大小的segment header结构,通过他可以找到本段对应的inode entry。

Trx_rseg_undo_slots:4096个字节,存undo链表first undopage 的页号,也就是undo slot集合。(一个页号占用四个字节,对于16kb的页面,这里存着1024个undo slot,所以一共需要1024*4=4096个字节)

File tailder:8个字节。

innoDB规定每个rollback segment header都属于一个段,这个段就是回滚段。与前面介绍段不同的是,这个rollback segment 其实只有一个页面。

从回滚段中申请undo页面链表

初始情况下,我们未向任何一个事务分配undo页面链表,所以对于rollback segment header中的各种undo slot都设置成一个特殊值fil_null(表示该undo slot不指向任何页面)。

随着项目的运行,开始有事务申请链表了,从回滚段的第一个开始看是不是fil_null:

如果是fil_null,那么在表空间创建一个新的段(也就是undo log segment),然后从段里申请一个页面作为undo页面链表的first undo page,然后把该undo slot设置为刚刚申请的这个页面的页号,意味着这个undo slot分配给了这个事务。

如果不是fil_null,说明该undo slot已经指向一个链表,也就是说已经被其他事务占用,于是看看下一个undo slot是否是fil_null。

已知rollback segment header有1024个undo slot,如果1024个值都不为fil_null,意味着都分配给了事务,此时无法在获取新的undo页面链表,则就会回滚这个事务给用户报错:

Too many active concurrent transactions

用户看到这个错误,可以重新执行这个事务(因为可能有其他事务已经提交,该事务就可以分配undo链表)。

当一个事务提交时,他所占用的undo slot有两种命运:

如果该undo slot指向的undo页面链表符合重用(里面只有一个页面,且小于总空间的3/4)

该undo slot就处于被缓存状态,这时候undo链表的trx_undo_state属性会被设置成trx_undo_cache(该属性在first undo page的undo log segment header)。

被缓存的undo slot会加入链表,如果是insert undo会加入insert undo cache链表,如果是update undo会加入update undo cache链表。

一个回滚段就对应着两个cache链表,如果有新事物需要分配undo slot会先去cache链表找,如果没找到,则再去回滚段rollback segment header页面中找。

如果该undo slot指向undo页面链表不符合被重用的条件,那么针对该undo slot对应的undo页面链表类型不同,会有不同的处理方式:

如果对应的是insert undo 链表,则该undo页面链表的trx_undo_state属性会被设置为trx_undo_to_free,之后该undo页面对应的段会被释放,意味着可以挪做他用,然后把undo slot的值设置为fil_null。

如果对应的是update undo链表,则该undo页面链表的trx_undo_state属性会被设置为trx_undo_to_pruge,之后则会吧undo slot的值设置为fil_null,然后把本次事务写入的一组undo日志放入所谓的history链表中。(注意这里不会释放挪做他用)

多个段回滚

前面说了一个事务最多分配四个undo页面链表,而一个回滚段有1024个undo slot,显示然slot数量有点少。也就是一个回滚段最多容纳1024个读写事务同时执行,再多就崩溃了。因为前面说过rollback unde segment段是固定只有一个页的,不可能一直无线增长。

早期确实只有一个回滚段,但随着业务和机器性能的提升,innoDB一口气定义128个回滚段,相当于128*1024 = 131072个undo slot,那么意思就是支持131072个事务同时执行。

每个回滚段都有一个rollback segment header,所以自然有128个rollback segment header,那么这些自然会统一存起来,于是innoDB在系统空间表第5个页面的某个区域包含了128个8个字节大小的格子,每个格子的构造大概就是:

Space id:4个字节大小,代表表空间的id。

Page number:4个子节大小,代表一个页号。

也就是每8个字节代表一个指针,指向rollback segment header,这里需要注意,不同的segment header对应不同的表空间id。

综上我们知道:系统表空间第五号页面存着128个rollback segment header页面地址,就相当于回滚段。每个回滚段里有1024个undo slot,每个undo slot对应一个undo页面链表。

回滚段分类

回滚段从0开始,吧128个编号的话,第一个就是第0号回滚段,最后一个就是127号回滚段。这128个分为两大类。

第0号、第33~127号属于一类,其中第0号必须在系统表空间,第33~127号既可以咋系统表空间,又可以在undo表空间。(如果一个事务在执行过程中由于对普通表记录做了改动需要分配undo页面链表时,必须从这一类段中分配相对应的undo slot)

第1~32号属于一类。这些必须在临时表空间中(对应的数据目录ibtmp1文件)。

如果一个事务对临时表修改了,则必须从这一类中分配相对应的undo slot。

也就是说两类回滚段一个管理者临时表,一个管理着普通表的undo slot。那么为什么要把普通表和临时表的undo slot分开呢?

因为undo页面本身类型就是fil_page_undo_log的页面简称,说到底他也是个普通的页面。我们前面说够,修改页面之前,一定要先把对应的redo日志写上,这样系统崩溃才能恢复数据。我们在写undo日志本身也是个写页面的过程,所以也会有redo日志记录,防止丢失,innoDB还设计了许多redo日志类型,如mlog_undo_hdr_ cache、mlog_undo_insert、mlog_undo_init等等,也就是说我们对undo页面做任何记录的改动都会记录相对应的redo日志。那么对于临时表,并不需要系统崩溃后恢复,所以临时表的undo日志写入时候,并没有记录redo日志。总结,对于undo日志的记录,会记录redo日志防止系统崩溃数据丢失,而临时表本身就是系统关闭所有数据不需要恢复,所以没有记录redo日志。

为事务分配undo页面链表详细过程

接下来我们以事务对普通表做改动为例,给大家梳理一下事务执行过程中分配undo页面链表时的完整过程。

首先事务在执行过程中对普通表做了首次改动,在系统表空间第5号页面中分配回滚段(就是获取一个rollback segment header页面地址)。一旦某个回滚段被分配了这个事务,那么之后该事务对普通表记录做修改,就不会重新分配。

使用传说中的round-robin(循环方式)获取rollback segment header,当第0个回滚段已经分配了事务,则就看第33个回滚段,依次看下一个。

分配了回滚段后,就要看这里面两个cache链表,如果insert就看insert undo cache链表,如果update 就看update undo cache链表里是否存在undo slot,如果有,则吧undo slot分配给该事务。

如果没有缓存undo slot,则就去rollback segment header页面找一个可用的undo slot分配给该事务。(前面也说了如果找undo slot,就是从第0个开始,如果不是ful_null,就依次找到1024个ful_null)

找到undo slot后,如果是缓存中找到的,说明已经分配了undo log segment,如果不是,则需要重新分配undo log segment,然后从undo log segment 申请一个first作为first undo page页面。

然后事务就可以吧undo日志写入undo页面链表里。

临时表对上亦如此,就不赘述。

回滚段相关配置

配置回滚段数量:

前面说过有128个回滚段,这是个默认值,可以通过innoDB_rollback_segments来配置回滚段,可配置1~128。但这个参数不影响临时回滚段,他一直是32。

也就是说如果这个参数设置为1,临时回滚段还是32.

如果这个参数在2~33之间,普通回滚段还是1.

如果这个参数大于33,则普通回滚段的数量是这个参数减去32的剩余数量。

配置undo表空间:

默认情况下,第0号、第33号到底127号都是在系统表空间。其中0号回滚段一直在系统表空间,但33~127号在自定义undo表空间。可以修改配置,但只能在初始化的时候创建,一旦分配好里面有数据就不能修改。

通过innoDB_undo_directory指定undo表空间所在目录,如果没有指定参数,则默认undo表空间所在目录是数据目录。

通过innoDB_undo_tablespace定义undo表空间数量。该参数默认为0,表明不创建任何undo表空间。

第33~127可以平分分不到不同的undo表空间。

比如我们设置innoDB_rollback_segments的参数是35,innodb_undo_tablespaces的为2,则33,和34号回滚段分布到一个undo表空间中。

设计undo表空间的好处是,会吧undo表空间截取truncate成一个个小文件,而系统表空间大小只会越拉越大。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-10-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 后端从入门到精通 微信公众号,前往查看

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 重用undo页面
  • Insert undo链表:
  • Update undo链表:
  • 多个段回滚
  • 回滚段分类
  • 回滚段相关配置
相关产品与服务
云数据库 SQL Server
腾讯云数据库 SQL Server (TencentDB for SQL Server)是业界最常用的商用数据库之一,对基于 Windows 架构的应用程序具有完美的支持。TencentDB for SQL Server 拥有微软正版授权,可持续为用户提供最新的功能,避免未授权使用软件的风险。具有即开即用、稳定可靠、安全运行、弹性扩缩等特点。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档