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

本文涉及的产品
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
简介: 重用的undo日志 (3)—mysql进阶(六十六)

前面说了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成一个个小文件,而系统表空间大小只会越拉越大。

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
18天前
|
存储 安全 关系型数据库
Mysql 的binlog日志的优缺点
MySQL的binlog(二进制日志)是一个记录数据库更改的日志文件,它包含了所有对数据库执行的更改操作,如INSERT、UPDATE和DELETE等。binlog的主要目的是复制和恢复。以下是binlog日志的优缺点: ### 优点: 1. **数据恢复**:当数据库出现意外故障或数据丢失时,可以利用binlog进行点恢复(point-in-time recovery),将数据恢复到某一特定时间点。 2. **主从复制**:binlog是实现MySQL主从复制功能的核心组件。主服务器将binlog中的事件发送到从服务器,从服务器再重放这些事件,从而实现数据的同步。 3. **审计**:b
|
28天前
|
SQL 关系型数据库 MySQL
MySQL数据库,可以使用二进制日志(binary log)进行时间点恢复
对于MySQL数据库,可以使用二进制日志(binary log)进行时间点恢复。二进制日志是MySQL中记录所有数据库更改操作的日志文件。要进行时间点恢复,您需要执行以下步骤: 1. 确保MySQL配置文件中启用了二进制日志功能。在配置文件(通常是my.cnf或my.ini)中找到以下行,并确保没有被注释掉: Copy code log_bin = /path/to/binary/log/file 2. 在需要进行恢复的时间点之前创建一个数据库备份。这将作为恢复的基准。 3. 找到您要恢复到的时间点的二进制日志文件和位置。可以通过执行以下命令来查看当前的二进制日志文件和位
|
1月前
|
存储 SQL 关系型数据库
[MySQL]事务原理之redo log,undo log
[MySQL]事务原理之redo log,undo log
|
1月前
|
SQL 缓存 关系型数据库
MySQL的万字总结(缓存,索引,Explain,事务,redo日志等)
MySQL的万字总结(缓存,索引,Explain,事务,redo日志等)
66 0
|
2天前
|
SQL 存储 关系型数据库
MySQL Cluster集群安装及使用
MySQL Cluster集群安装及使用
|
17天前
|
关系型数据库 MySQL 数据库
mysql卸载、下载、安装(window版本)
mysql卸载、下载、安装(window版本)
|
1月前
|
关系型数据库 MySQL 数据库连接
关于MySQL-ODBC的zip包安装方法
关于MySQL-ODBC的zip包安装方法
|
1月前
|
关系型数据库 MySQL 数据库
rds安装数据库客户端工具
安装阿里云RDS的数据库客户端涉及在本地安装对应类型(如MySQL、PostgreSQL)的客户端工具。对于MySQL,可选择MySQL Command-Line Client或图形化工具如Navicat,安装后输入RDS实例的连接参数进行连接。对于PostgreSQL,可以使用`psql`命令行工具或图形化客户端如PgAdmin。首先从阿里云控制台获取连接信息,然后按照官方文档安装客户端,最后配置客户端连接以确保遵循安全指引。
86 1
|
6天前
|
关系型数据库 MySQL 数据库
《MySQL 简易速速上手小册》第1章:MySQL 基础和安装(2024 最新版)
《MySQL 简易速速上手小册》第1章:MySQL 基础和安装(2024 最新版)
28 4
|
30天前
|
Ubuntu 关系型数据库 MySQL
Ubuntu 中apt 安装MySQL数据库
Ubuntu 中apt 安装MySQL数据库
69 0