暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

[MYSQL] mysql undo文件解析(1)

原创 大大刺猬 2024-08-02
658

导读

之前解析过mysql的各种文件, 比如:ibd,redo,binlog,frm,myd. 貌似漏了个undo文件没有解析… 现在来补上 -_-
第一篇先简单看下, 尽量不涉及到具体的页解析

介绍

undo日志是要un do的时候使用的日志. 我们可以使用innodb_undo_directory控制undo日志的路径(默认在@@datadir目录下), 可以使用innodb_undo_tablespaces控制undo的文件数量.

undo里面记录的是修改前的数据(MVCC实现).每个事务最多分配4个undo日志. (主要分为insert/update(含delete)两种, 但又涉及到临时表, 所以可以看作是4种).

Each undo tablespace and the global temporary tablespace individually support a maximum of 128 rollback segments. The innodb_rollback_segments variable defines the number of rollback segments.

既然段的数量是有限的,那么支持的事务数量也就是有限的了. 官方给了个计算方式:

-- 如果每个事务都只有Insert或者update/delete (innodb_page_size / 16) * innodb_rollback_segments * number of undo tablespaces = (16384/16)*128*2 = 262144 -- 如果每个事务都有insert和update/delete (innodb_page_size / 16 / 2) * innodb_rollback_segments * number of undo tablespaces = (16384/16/2)*128*2 = 131072 -- 如果每个事务都是对临时表做insert (不存在只update/insert临时表) (innodb_page_size / 16) * innodb_rollback_segments = (16384/16)*128 = 131072 -- 如果每个事务都是对临时表做insert和update/delete (innodb_page_size / 16 / 2) * innodb_rollback_segments = (16384/16/2)*128 = 65536

看起来支持的事务数量是有限的, 但一般达不到这么多的事务量, 所以也不用关心这个.

按65536看, 那都得至少65536个连接在跑事务

undo_001

扯远了, 我们还是来看看undo的文件结构吧
image.png

我这是两个undo文件, 均为16MB(实际上可能某一个会大很多). undo也是innodb实现的,那么应该和ibd文件之类的格式类似, 我们使用如下python代码解析下:

import struct filename = '/data/mysql_dev/data/undo_001' f = open(filename,'rb') linesize = 20 f.seek(0,0) for i in range(int(os.stat(filename).st_size/16384)): bdata = f.read(16384) FIL_PAGE_SPACE_OR_CHKSUM, FIL_PAGE_OFFSET, FIL_PAGE_PREV, FIL_PAGE_NEXT, FIL_PAGE_LSN, FIL_PAGE_TYPE, FIL_PAGE_FILE_FLUSH_LSN, FIL_PAGE_SPACE_ID = struct.unpack('>4LQHQL',bdata[:38]) print(f'{FIL_PAGE_TYPE} ',end = '\n' if i%linesize == 0 else '')

image.png

可以看到
第一页是8(FIL_PAGE_TYPE_FSP_HDR),
第二页是5(FIL_PAGE_IBUF_BITMAP),
第三页是3(FIL_PAGE_INODE) – ibd文件记录索引段的
和ibd文件是一样的.
第4页是21(FIL_PAGE_TYPE_RSEG_ARRAY) 是undo特有的页(Rollback Segment Array page )
剩下的都是 6(FIL_PAGE_TYPE_SYS) 2(FIL_PAGE_UNDO_LOG)和0(FIL_PAGE_TYPE_ALLOCATED)了.
也就是我们只需要再了解FIL_PAGE_TYPE_RSEG_ARRAY,FIL_PAGE_TYPE_SYS和FIL_PAGE_UNDO_LOG 就可以解析undo文件了.

先不急, 我们利用下之前的工具ibd2sql的debug功能来获取下rollptr.
我们先准备下测试表吧

create table t20240802(id int, name varchar(200)); insert into t20240802 values(1,'ddcw'); insert into t20240802 values(2,'ddcw'); insert into t20240802 values(3,'ddcw'); update t20240802 set name='newddcw' where id=2;

然后使用ibd2sql解析这行被修改的数据

python3 main.py /data/mysql_dev/data/db1/t20240802.ibd --sql --debug

image.png

这个回滚指针roll pointer的格式为:

对象 大小 描述
offset 2字节 在page中的位置
page_no 4字节 在哪个页面
rseg_id 7bit rollback segment ID
is_insert 1bit 是否为insert

所以我们可以使用如下python代码解析回滚指针

rolll_ptr = 562949973550095 offset = rolll_ptr & 0xFFFF page_no = (rolll_ptr>>16) & 0xFFFFFFFF rseg_id = (rolll_ptr>>48) & 0x7F is_insert = True if rolll_ptr>>55 == 1 else False print(f"PAGENO:{page_no} OFFSET:{offset} rseg_id:{rseg_id} is_insert:{is_insert}")

image.png

然后我们去undo里面对应的位置解析瞧瞧

import struct filename = '/data/mysql_dev/data/undo_002' f = open(filename,'rb') f.seek(page_no*16384,0) data = f.read(16384) data[offset:offset+100]

image.png

这个record格式前两字节为下一record位置(绝对), 最后2字节为上一record位置(绝对)
image.png

保存的数据是使用lv格式的, 即长度+数据(后面再讲吧. 这里我们就已经看到我们删除前的数据了).

参考:
https://dev.mysql.com/doc/refman/8.0/en/innodb-undo-logs.html
https://dev.mysql.com/doc/refman/8.0/en/innodb-parameters.html
https://github.com/mysql/mysql-server/blob/trunk/storage/innobase

「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论