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

本文涉及的产品
RDS Agent(兼容Hermes Agent),2核4GB
RDS MySQL DuckDB 分析主实例,集群系列 4核8GB
RDS DuckDB + QuickBI 企业套餐,8核32GB + QuickBI 专业版
简介: 重用的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成一个个小文件,而系统表空间大小只会越拉越大。

相关实践学习
【涂鸦即艺术】基于云应用开发平台CAP部署AI实时生图绘板
【涂鸦即艺术】基于云应用开发平台CAP部署AI实时生图绘板
相关文章
|
9月前
|
SQL 运维 关系型数据库
深入探讨MySQL的二进制日志(binlog)选项
总结而言,对MySQL binlogs深度理解并妥善配置对数据库运维管理至关重要;它不仅关系到系统性能优化也是实现高可靠性架构设计必须考虑因素之一。通过精心规划与周密部署可以使得该机能充分发挥作用而避免潜在风险带来影响。
307 6
|
SQL 监控 关系型数据库
MySQL日志分析:binlog、redolog、undolog三大日志的深度探讨。
数据库管理其实和写小说一样,需要规划,需要修订,也需要有能力回滚。理解这些日志的作用与优化,就像把握写作工具的使用与运用,为我们的数据库保驾护航。
917 23
|
SQL 运维 关系型数据库
MySQL Binlog 日志查看方法及查看内容解析
本文介绍了 MySQL 的 Binlog(二进制日志)功能及其使用方法。Binlog 记录了数据库的所有数据变更操作,如 INSERT、UPDATE 和 DELETE,对数据恢复、主从复制和审计至关重要。文章详细说明了如何开启 Binlog 功能、查看当前日志文件及内容,并解析了常见的事件类型,包括 Format_desc、Query、Table_map、Write_rows、Update_rows 和 Delete_rows 等,帮助用户掌握数据库变化历史,提升维护和排障能力。
|
存储 SQL 关系型数据库
mysql的undo log、redo log、bin log、buffer pool
MySQL的undo log、redo log、bin log和buffer pool是确保数据库高效、安全和可靠运行的关键组件。理解这些组件的工作原理和作用,对于优化数据库性能和保障数据安全具有重要意义。通过适当的配置和优化,可以显著提升MySQL的运行效率和数据可靠性。
285 4
|
SQL 存储 关系型数据库
简单聊聊MySQL的三大日志(Redo Log、Binlog和Undo Log)各有什么区别
在MySQL数据库管理中,理解Redo Log(重做日志)、Binlog(二进制日志)和Undo Log(回滚日志)至关重要。Redo Log确保数据持久性和崩溃恢复;Binlog用于主从复制和数据恢复,记录逻辑操作;Undo Log支持事务的原子性和隔离性,实现回滚与MVCC。三者协同工作,保障事务ACID特性。文章还详细解析了日志写入流程及可能的异常情况,帮助深入理解数据库日志机制。
1965 0
|
XML 安全 Java
【日志框架整合】Slf4j、Log4j、Log4j2、Logback配置模板
本文介绍了Java日志框架的基本概念和使用方法,重点讨论了SLF4J、Log4j、Logback和Log4j2之间的关系及其性能对比。SLF4J作为一个日志抽象层,允许开发者使用统一的日志接口,而Log4j、Logback和Log4j2则是具体的日志实现框架。Log4j2在性能上优于Logback,推荐在新项目中使用。文章还详细说明了如何在Spring Boot项目中配置Log4j2和Logback,以及如何使用Lombok简化日志记录。最后,提供了一些日志配置的最佳实践,包括滚动日志、统一日志格式和提高日志性能的方法。
5044 32
【日志框架整合】Slf4j、Log4j、Log4j2、Logback配置模板
|
监控 安全 Apache
什么是Apache日志?为什么Apache日志分析很重要?
Apache是全球广泛使用的Web服务器软件,支持超过30%的活跃网站。它通过接收和处理HTTP请求,与后端服务器通信,返回响应并记录日志,确保网页请求的快速准确处理。Apache日志分为访问日志和错误日志,对提升用户体验、保障安全及优化性能至关重要。EventLog Analyzer等工具可有效管理和分析这些日志,增强Web服务的安全性和可靠性。
627 9
|
监控 容灾 算法
阿里云 SLS 多云日志接入最佳实践:链路、成本与高可用性优化
本文探讨了如何高效、经济且可靠地将海外应用与基础设施日志统一采集至阿里云日志服务(SLS),解决全球化业务扩展中的关键挑战。重点介绍了高性能日志采集Agent(iLogtail/LoongCollector)在海外场景的应用,推荐使用LoongCollector以获得更优的稳定性和网络容错能力。同时分析了多种网络接入方案,包括公网直连、全球加速优化、阿里云内网及专线/CEN/VPN接入等,并提供了成本优化策略和多目标发送配置指导,帮助企业构建稳定、低成本、高可用的全球日志系统。
1317 55

推荐镜像

更多