之前讲了mysql恢复drop/truncate表的方法, 但对于8.0的truncate却比较麻烦, 主要是indexid不好确认, 当时提示了下可以找redo.
其实update操作是不写之前的信息到redo的, 但是要将update前的信息写到undo, 而写undo的过程是要记录redo的, 所以我们还是可以解析redo获取到update之前的操作(即找到被truncate的表).
3年前,我们有解析过redo,虽然比较简单. 这次我们来补全下,顺便整个redoreader工具来解析mysql redo log, 考虑到还有不少使用5.7的, 所以我们兼容5.7,8.0等版本.
一个事务由若干条SQL组成, 一条SQL由若干个mtr(mini-transaction)组成, 一个mtr由若干个mlog组成. 若干个mlog放在若干个block里面. 若干个block组成一个redo log file, 若干个redo log file构成redo(group).
8.0.30之前redo文件是循环写的, 8.0.30及其之后是32个redo文件构成一个组的. 简单点理解就是由几个循环写的redo文件变成了一堆redo文件. 之前的版本是只有0号redolog才有log header,由于log header会记录一些关键信息, 所以会经常写(可观察其时间戳), 后面的版本是每个都有,类似binlog那种了
一图以言之:

这里的校验(LOG_BLOCK_CHECKSUM)是使用的crc32c算法(innodb的校验基本上都是用的这玩意.)
来看看具体的mlog, 先看下整体结构:
对象 | 大小(字节) | 描述 |
|---|---|---|
mlog_type | 1 字节 | 第1bit记录是否是单个mlog作为mtr,后7bit就是事务类型 |
spaceno | mach_parse_compressed | 操作的表空间号 |
pageno | mach_parse_compressed | 操作的表空间的页号 |
data | n | 不同的mlog有不同的data,也是本文的重点之一. |
spaceno和pageno是大部分mlog都有的, 但MLOG_MULTI_REC_END,MLOG_TABLE_DYNAMIC_META,MLOG_CHECKPOINT是不需要的,也就没有
一共有1-76(不含3,5,6,7,12)即72种mlog. 我们全都解析! 我们主要参考源码中的函数recv_parse_or_apply_log_rec_body(storage/innobase/log/log0recv.cc)
由于mtr太多,每个都拎出来的话,看起比较烦,我就直接整个表格吧: 带有8027的表示老版本使用的,新版本结构发生了变化.
mlog编号 | mlog类型 | mlog结构 | 描述 |
|---|---|---|---|
1 | MLOG_1BYTE | mlog_parse_nbytes | 记录1字节的修改 |
2 | MLOG_2BYTES | mlog_parse_nbytes | 记录2字节的修改 |
4 | MLOG_4BYTES | mlog_parse_nbytes | 记录4字节的修改 |
8 | MLOG_8BYTES | mlog_parse_nbytes | 记录8字节的修改 |
9 | MLOG_REC_INSERT_8027 | parse_index_8027(False) page_cur_parse_insert_rec | 记录insert操作的 |
10 | MLOG_REC_CLUST_DELETE_MARK_8027 | parse_index_8027(False) btr_cur_parse_del_mark_set_clust_rec | 对主键索引的某行做delete标记 |
11 | MLOG_REC_SEC_DELETE_MARK | value:1字节 offset:2字节 | 对二级索引的某行做delete标记 |
13 | MLOG_REC_UPDATE_IN_PLACE_8027 | parse_index_8027(False) btr_cur_parse_update_in_place | update of a record, preserves record field sizes |
14 | MLOG_REC_DELETE_8027 | parse_index_8027(False) page_cur_parse_delete_rec | delete一行 |
15 | MLOG_LIST_END_DELETE_8027 | parse_index_8027(False) offset:2字节 | delete一行(结束标记) |
16 | MLOG_LIST_START_DELETE_8027 | parse_index_8027(False) page_parse_delete_rec_list | delete一行(开始标记) |
17 | MLOG_LIST_END_COPY_CREATED_8027 | parse_index_8027(False) page_parse_copy_rec_list_to_created_page | Copy record list end to a new created index page |
18 | MLOG_PAGE_REORGANIZE_8027 | parse_index_8027(False) btr_parse_page_reorganize(False) | 重建索引(row_format=REDUNDANT) |
19 | MLOG_PAGE_CREATE | 创建一个索引页 | |
20 | MLOG_UNDO_INSERT | undo_len:2字节 undo_data:undo_len | 写undo(我们恢复truncate表就看这个) |
21 | MLOG_UNDO_ERASE_END | erase an undo log page end | |
22 | MLOG_UNDO_INIT | type:mach_parse_compressed | 初始化undo页 |
23 | MLOG_UNDO_HDR_DISCARD | discard an update undo log header | |
24 | MLOG_UNDO_HDR_REUSE | trx_id:mach_u64_parse_compressed | reuse an insert undo log header |
25 | MLOG_UNDO_HDR_CREATE | trx_id:mach_u64_parse_compressed | 创建一个undo log header |
26 | MLOG_REC_MIN_MARK | rec:2字节 | 定义一个最小字段 |
27 | MLOG_IBUF_BITMAP_INIT | 初始化一个ibuf bitmap页 | |
28 | MLOG_LSN | mtr_header | 记录当前的lsn |
29 | MLOG_INIT_FILE_PAGE | ||
30 | MLOG_WRITE_STRING | offset:2 length:2 data:length | 写一个字符串 |
31 | MLOG_MULTI_REC_END | 一个mtr结束的mlog | |
32 | MLOG_DUMMY_RECORD | dummy log record used to pad a log block full | |
33 | MLOG_FILE_CREATE | flags:4 filename_len:2 filename:filename_len | log record about an .ibd file creation |
34 | MLOG_FILE_RENAME | from_len:2 from_name:from_len to_len:2 to_name:to_len | rename databasename/tablename |
35 | MLOG_FILE_DELETE | filename_len:2 filename:filename_len | 删除一个表空间文件 |
36 | MLOG_COMP_REC_MIN_MARK | rec:2 | 定义一个最小字段(compact) |
37 | MLOG_COMP_PAGE_CREATE | 创建一个索引页(compact) | |
38 | MLOG_COMP_REC_INSERT_8027 | parse_index_8027(True) page_cur_parse_insert_rec | 记录insert操作的(compact) |
39 | MLOG_COMP_REC_CLUST_DELETE_MARK_8027 | parse_index_8027(True) btr_cur_parse_del_mark_set_clust_rec | 对主键索引的某行做delete标记(compact) |
40 | MLOG_COMP_REC_SEC_DELETE_MARK | parse_index_8027(True) | 对二级索引的某行做delete标记(compact) |
41 | MLOG_COMP_REC_UPDATE_IN_PLACE_8027 | parse_index_8027(True) btr_cur_parse_update_in_place | update(compact) |
42 | MLOG_COMP_REC_DELETE_8027 | parse_index_8027(True) page_cur_parse_delete_rec | delete(compact) |
43 | MLOG_COMP_LIST_END_DELETE_8027 | parse_index_8027(True) page_parse_delete_rec_lis | delete结束(compact) |
44 | MLOG_COMP_LIST_START_DELETE_8027 | parse_index_8027(True) page_parse_delete_rec_list | delete开始(compact) |
45 | MLOG_COMP_LIST_END_COPY_CREATED_8027 | parse_index_8027(True) page_parse_copy_rec_list_to_created_page | copy compact record list end to a new created index page |
46 | MLOG_COMP_PAGE_REORGANIZE_8027 | parse_index_8027(True) btr_parse_page_reorganize(False) | reorganize an index page |
47 | MLOG_FILE_CREATE2 | fil_name_parse | 创建一个ibd文件 |
48 | MLOG_ZIP_WRITE_NODE_PTR | offset:2字节 z_offset:2字节 data:4字节 | 节点指针(压缩的非叶子节点) |
49 | MLOG_ZIP_WRITE_BLOB_PTR | offset:2字节 z_offset:2字节 data:20字节 | 压缩的溢出页指针 |
50 | MLOG_ZIP_WRITE_HEADER | offset:1 len:1 data:len | 压缩页页头 |
51 | MLOG_ZIP_PAGE_COMPRESS | page_size:2 trailer_size:2 page_pre:4 page_next:4 page_data:page_size trailer_data:trailer_size | 压缩一个索引页 |
52 | MLOG_ZIP_PAGE_COMPRESS_NO_DATA_8027 | parse_index_8027(True) page_zip_parse_compress_no_data | 压缩一个索引页(nodata) |
53 | MLOG_ZIP_PAGE_REORGANIZE_8027 | parse_index_8027(True) btr_parse_page_reorganize(True) | 重建一个压缩页 |
54 | MLOG_FILE_RENAME2 | fil_name_parse | 重命名一个表空间文件 |
55 | MLOG_FILE_NAME | fil_name_parse | note the first use of a tablespace file since checkpoint |
56 | MLOG_CHECKPOINT | lsn:8 | lsn |
57 | MLOG_PAGE_CREATE_RTREE | 创建一个Rtree索引页 | |
58 | MLOG_COMP_PAGE_CREATE_RTREE | 创建一个Rtree索引页(compact) | |
59 | MLOG_INIT_FILE_PAGE2 | MLOG_INIT_FILE_PAGE | |
60 | MLOG_TRUNCATE | lsn:8 | Disabled for WL6378 |
61 | MLOG_INDEX_LOAD | data:8 | |
62 | MLOG_TABLE_DYNAMIC_META | tableid:mach_parse_u64_much_compressed version:mach_parse_u64_much_compressed persister:1 autoinc:mach_parse_u64_much_compressed | 持久化动态元数据信息 注:这个及其之后的Mlog是8.0才有的 |
63 | MLOG_PAGE_CREATE_SDI | 创建一个sdi索引页 | |
64 | MLOG_COMP_PAGE_CREATE_SDI | 创建一个sdi索引页(compact) | |
65 | MLOG_FILE_EXTEND | offset:8 size:8 | 扩展表空间 |
66 | MLOG_TEST | key:8 value:8 payload_size:2 payload:payload_size start_lsn:8 end_lsn:8 | tests of redo log. |
67 | MLOG_REC_INSERT | parse_index page_cur_parse_insert_rec(False) | |
68 | MLOG_REC_CLUST_DELETE_MARK | parse_index btr_cur_parse_del_mark_set_clust_rec | |
69 | MLOG_REC_DELETE | parse_index offset:2 | |
70 | MLOG_REC_UPDATE_IN_PLACE | parse_index btr_cur_parse_update_in_place | |
71 | MLOG_LIST_END_COPY_CREATED | parse_index log_data_len:4 data:log_data_len | |
72 | MLOG_PAGE_REORGANIZE | parse_index level:0(固定6) | |
73 | MLOG_ZIP_PAGE_REORGANIZE | parse_index btr_parse_page_reorganize(True) | |
74 | MLOG_ZIP_PAGE_COMPRESS_NO_DATA | parse_index page_zip_parse_compress_no_data | |
75 | MLOG_LIST_END_DELETE | parse_index offset:2 | |
76 | MLOG_LIST_START_DELETE | parse_index offset:2 | |
看起来多, 实际上就那么几种,很多都是新版本(instant)改了一丢丢, 旧版本就多个compact之类的.
再通过之前的ibd学习, 看到这些mlog就会格外的亲切.
对于spaceno,pageno等会大量出现,而且大部分都是固定的,为了节省空间,就需要对其进行简单的"压缩"处理(系统表空间id也经常使用,所以压缩的时候也得考虑这个)
上面有一堆比较通用的结构,我们这里来一个个瞅一下:
parse_index
8.0使用的解析index信息的
对象 | 大小(字节) |
|---|---|
index_version | 1 |
index_flags | 1 |
cols | 2 |
inst_cols | 2 (if index_flags&0x04) |
n_uniq | 2 |
index_fields | 2*cols |
index_versioned_fields | 2*inst_cols |
老版本的解析index(compact)则为:
parse_index_8027
对象 | 大小(字节) |
|---|---|
n | 2 |
n | 2 (if n & 0x8000) |
n_uniq | 2 |
col | 2*n |
来看下insert操作需要的:
page_cur_parse_insert_rec
对象 | 大小(字节) |
|---|---|
offset | 1 (if not is_short) |
end_seg_len | mach_parse_compressed |
info_and_status_bits | 1 (if end_seg_len & 0x1) |
origin_offset | mach_parse_compressed (if end_seg_len & 0x1) |
mismatch_index | mach_parse_compressed (if end_seg_len & 0x1) |
data | end_seg_len |
delete操作基本上就是一个offset:
page_cur_parse_delete_rec
对象 | 大小(字节) |
|---|---|
offset | 2 |
copy record操作:
page_parse_copy_rec_list_to_created_page
对象 | 大小(字节) |
|---|---|
log_data_len | 4 |
data | log_data_len |
delete操作(起始/结束):
page_parse_delete_rec_list
对象 | 大小(字节) |
|---|---|
offset | 2 |
重建page的操作
btr_parse_page_reorganize
对象 | 大小(字节) |
|---|---|
level | 2 (如果压缩的话) |
主键索引标记delete
btr_cur_parse_del_mark_set_clust_rec
对象 | 大小(字节) |
|---|---|
flags | 1 |
val | 1 |
pos | mach_parse_compressed |
roll_ptr | 7 |
trx_id | mach_u64_parse_compressed |
offset | 2 |
update操作:
btr_cur_parse_update_in_place
对象 | 大小(字节) |
|---|---|
flags | 1 |
pos | mach_parse_compressed |
roll_ptr | 7 |
trx_id | mach_u64_parse_compressed |
rec_offset | 2 |
info_bits | 1 |
n_fields | mach_parse_compressed |
field_no | mach_parse_compressed*n_fields |
field_len | mach_parse_compressed*n_fields |
field_value | field_len*n_fields |
压缩一个索引页
page_zip_parse_compress_no_data
对象 | 大小(字节) |
|---|---|
level | 1 |
文件相关的操作:
fil_name_parse
对象 | 大小(字节) |
|---|---|
4(if MLOG_FILE_CREATE2) | |
len | 2 |
name | len |
new_len | 2 (if MLOG_FILE_RENAME2) |
new_name | new_len (if MLOG_FILE_RENAME2) |
感觉使用python表示更直观一些, 但可能就比较水了...
对于经常出现的spaceno,pageno等整形是可以简单压缩下的(节省空间). 如果只是按照类似连接协议那种方式的压缩的话, 那么undo(4294967279),mysql.ibd(4294967294)就得使用老长的空间了. 所以采取了折中的压缩方式: 对小/大的数字采用短一点的编码. 说起来比较复杂, 我们直接使用python代码来表示4字节以内的压缩方式吧:
def rmach_parse_compressed(data):
val = data[0]
n = 1
if val < 0x80:
n = 1
elif val < 0xC0:
val = struct.unpack('>H',data[:2])[0] & 0x3FFF
n = 2
elif val < 0xE0:
val = struct.unpack('>L',b'\x00'+data[:3])[0] & 0x1FFFFF
n = 3
elif val < 0xF0:
val = struct.unpack('>L',data[:4])[0] & 0xFFFFFFF
n = 4
elif val < 0xF8:
val = struct.unpack('>L',data[1:5])[0]
n = 5
elif val < 0xFC:
val = (struct.unpack('>H',data[:2])[0] & 0x3FF) | 0xFFFFFC00
n = 2
elif val < 0xFE:
val = (struct.unpack('>L',b'\x00'+data[:3])[0] & 0x1FFFF) | 0xFFFE0000
n = 3
else:
val = (struct.unpack('>L',b'\x00'+data[1:4]))[0] | 0xFF000000
n = 4
return n,val也就是使用1-5字节来表示4字节的数字. 有了4字节的数字, 那么8字节的就简单了:
8字节的压缩有2种方式:
mach_u64_parse_compressed: 即对前4字节采取4字节压缩方式(mach_parse_compressed), 后4字节不变, 这样就是使用5-9字节表示8字节..
def mach_u64_parse_compressed(self):
return (self.mach_read_next_compressed()<<32)|self.mach_read_from_n(4)mach_parse_u64_much_compressed: 如果第1字节是0xff则直接使用mach_parse_compressed. 否则前后4字节都使用mach_parse_compressed来表示. 就只有MLOG_TABLE_DYNAMIC_META在使用...
def mach_parse_u64_much_compressed(self):
if self.offset == 508: # 可能跨Block
self._read_block()
self.offset = 12
if self.data[self.offset] != 0xFF:
return self.mach_parse_compressed()
n = self.mach_parse_compressed()
n << 32
return n|self.mach_parse_compressed()讲了那么多理论. 我们来动动手. 于是很快就写了个redo_reader: https://github.com/ddcw/redo
wget https://github.com/ddcw/redo/archive/refs/heads/main.zip
unzip main.zip
cd redo-main/
python3 redo_reader.py --help我们想看某条SQL语句执行的时候,redo进行了哪些操作:
获取起止LSN: (尽量保证只有你的SQL在跑,不然干扰太多)
-- 查看当前LSN
show engine innodb status\G
-- 执行任意SQL
insert into db1.t20260227 values(1);
-- 查看当前LSN
show engine innodb status\G
然后解析对应的redo log(不知道是哪个的话, 看时间戳就是了.或者直接*)
python3 redo_reader.py /data/mysql_8037/mysqllog/redolog/#innodb_redo/#ib_redo40 --start-lsn 674459317 --stop-lsn 674460135虽然只是简单的insert,但输出内容是非常多的,(truncate更多). 详情如下:
[root@ddcw21 redo-main]#python3 redo_reader.py /data/mysql_8037/mysqllog/redolog/#innodb_redo/#ib_redo40 --start-lsn 674459317 --stop-lsn 674460135
- [M] file:#ib_redo40 gblockno:1317304 lsn:674459317 cblockno:6731 offset:181 name:MLOG_4BYTES(4)
spaceno : 4294967278
pageno : 30
offset : 50
value : 4294967295
[M] file:#ib_redo40 gblockno:1317304 lsn:674459325 cblockno:6731 offset:189 name:MLOG_2BYTES(2)
spaceno : 4294967278
pageno : 30
offset : 54
value : 0
[M] file:#ib_redo40 gblockno:1317304 lsn:674459332 cblockno:6731 offset:196 name:MLOG_4BYTES(4)
spaceno : 4294967278
pageno : 30
offset : 56
value : 4294967295
[M] file:#ib_redo40 gblockno:1317304 lsn:674459340 cblockno:6731 offset:204 name:MLOG_2BYTES(2)
spaceno : 4294967278
pageno : 30
offset : 60
value : 0
[M] file:#ib_redo40 gblockno:1317304 lsn:674459347 cblockno:6731 offset:211 name:MLOG_4BYTES(4)
spaceno : 4294967278
pageno : 30
offset : 46
value : 0
[M] file:#ib_redo40 gblockno:1317304 lsn:674459354 cblockno:6731 offset:218 name:MLOG_MULTI_REC_END(31)
spaceno : -1
pageno : -1
- [S] file:#ib_redo40 gblockno:1317304 lsn:674459355 cblockno:6731 offset:219 name:MLOG_8BYTES(8)
spaceno : 0
pageno : 7
offset : 38
value : 12288
- [M] file:#ib_redo40 gblockno:1317304 lsn:674459365 cblockno:6731 offset:229 name:MLOG_UNDO_HDR_REUSE(24)
spaceno : 4294967278
pageno : 146
trx_id : 16715
[M] file:#ib_redo40 gblockno:1317304 lsn:674459375 cblockno:6731 offset:239 name:MLOG_2BYTES(2)
spaceno : 4294967278
pageno : 146
offset : 40
value : 272
[M] file:#ib_redo40 gblockno:1317304 lsn:674459384 cblockno:6731 offset:248 name:MLOG_2BYTES(2)
spaceno : 4294967278
pageno : 146
offset : 42
value : 272
[M] file:#ib_redo40 gblockno:1317304 lsn:674459393 cblockno:6731 offset:257 name:MLOG_2BYTES(2)
spaceno : 4294967278
pageno : 146
offset : 104
value : 272
[M] file:#ib_redo40 gblockno:1317304 lsn:674459402 cblockno:6731 offset:266 name:MLOG_MULTI_REC_END(31)
spaceno : -1
pageno : -1
- [S] file:#ib_redo40 gblockno:1317304 lsn:674459403 cblockno:6731 offset:267 name:MLOG_UNDO_INSERT(20)
spaceno : 4294967278
pageno : 146
len : 11
data : b'\x0b\x00\x84\xd7\x06\x00\x00\x00\x000\x00'
- [S] file:#ib_redo40 gblockno:1317304 lsn:674459421 cblockno:6731 offset:285 name:MLOG_REC_INSERT(67)
spaceno : 177
pageno : 4
index_version: 1
index_flags: 1
cols : 4
inst_cols : None
n_uniq : 1
index_fields: [32774, 32774, 32775, 4]
index_versioned_fields: None
offset : 99
end_seg_len: 29
info_and_status_bits: 0
origin_offset: 6
mismatch_index: 0
data : b'\x00\x00\x00\x10\xff\xf2\x00\x00\x00\x000\x00\x00\x00\x00\x00AK\x82\x00\x00\x00\x92\x01\x10\x80\x00\x00\x01'
- [M] file:#ib_redo40 gblockno:1317304 lsn:674459474 cblockno:6731 offset:338 name:MLOG_2BYTES(2)
spaceno : 4294967278
pageno : 146
offset : 56
value : 6
[M] file:#ib_redo40 gblockno:1317304 lsn:674459482 cblockno:6731 offset:346 name:MLOG_1BYTE(1)
spaceno : 4294967278
pageno : 146
offset : 106
value : 1
[M] file:#ib_redo40 gblockno:1317304 lsn:674459490 cblockno:6731 offset:354 name:MLOG_4BYTES(4)
spaceno : 4294967278
pageno : 146
offset : 132
value : 1
[M] file:#ib_redo40 gblockno:1317304 lsn:674459498 cblockno:6731 offset:362 name:MLOG_4BYTES(4)
spaceno : 4294967278
pageno : 146
offset : 136
value : 24
[M] file:#ib_redo40 gblockno:1317304 lsn:674459506 cblockno:6731 offset:370 name:MLOG_4BYTES(4)
spaceno : 4294967278
pageno : 146
offset : 140
value : 0
[M] file:#ib_redo40 gblockno:1317304 lsn:674459514 cblockno:6731 offset:378 name:MLOG_WRITE_STRING(30)
spaceno : 4294967278
pageno : 146
offset : 144
length : 128
data : b'MySQLXid%\xa9\xb0\x19\x00\x00\x00\x00Q\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
[M] file:#ib_redo40 gblockno:1317305 lsn:674459667 cblockno:6732 offset:019 name:MLOG_MULTI_REC_END(31)
spaceno : -1
pageno : -1
- [M] file:#ib_redo40 gblockno:1317305 lsn:674459668 cblockno:6732 offset:020 name:MLOG_UNDO_HDR_CREATE(25)
spaceno : 4294967278
pageno : 293
trx_id : 16715
[M] file:#ib_redo40 gblockno:1317305 lsn:674459678 cblockno:6732 offset:030 name:MLOG_2BYTES(2)
spaceno : 4294967278
pageno : 293
offset : 40
value : 3578
[M] file:#ib_redo40 gblockno:1317305 lsn:674459687 cblockno:6732 offset:039 name:MLOG_2BYTES(2)
spaceno : 4294967278
pageno : 293
offset : 42
value : 3578
[M] file:#ib_redo40 gblockno:1317305 lsn:674459696 cblockno:6732 offset:048 name:MLOG_2BYTES(2)
spaceno : 4294967278
pageno : 293
offset : 3345
value : 3578
[M] file:#ib_redo40 gblockno:1317305 lsn:674459705 cblockno:6732 offset:057 name:MLOG_MULTI_REC_END(31)
spaceno : -1
pageno : -1
- [M] file:#ib_redo40 gblockno:1317305 lsn:674459706 cblockno:6732 offset:058 name:MLOG_2BYTES(2)
spaceno : 4294967278
pageno : 146
offset : 56
value : 2
[M] file:#ib_redo40 gblockno:1317305 lsn:674459714 cblockno:6732 offset:066 name:MLOG_2BYTES(2)
spaceno : 4294967278
pageno : 293
offset : 56
value : 2
[M] file:#ib_redo40 gblockno:1317305 lsn:674459722 cblockno:6732 offset:074 name:MLOG_4BYTES(4)
spaceno : 4294967278
pageno : 34
offset : 50
value : 293
[M] file:#ib_redo40 gblockno:1317305 lsn:674459730 cblockno:6732 offset:082 name:MLOG_2BYTES(2)
spaceno : 4294967278
pageno : 34
offset : 54
value : 3361
[M] file:#ib_redo40 gblockno:1317305 lsn:674459738 cblockno:6732 offset:090 name:MLOG_4BYTES(4)
spaceno : 4294967278
pageno : 34
offset : 56
value : 293
[M] file:#ib_redo40 gblockno:1317305 lsn:674459746 cblockno:6732 offset:098 name:MLOG_2BYTES(2)
spaceno : 4294967278
pageno : 34
offset : 60
value : 3361
[M] file:#ib_redo40 gblockno:1317305 lsn:674459754 cblockno:6732 offset:106 name:MLOG_4BYTES(4)
spaceno : 4294967278
pageno : 293
offset : 3361
value : 4294967295
[M] file:#ib_redo40 gblockno:1317305 lsn:674459763 cblockno:6732 offset:115 name:MLOG_2BYTES(2)
spaceno : 4294967278
pageno : 293
offset : 3365
value : 0
[M] file:#ib_redo40 gblockno:1317305 lsn:674459771 cblockno:6732 offset:123 name:MLOG_4BYTES(4)
spaceno : 4294967278
pageno : 293
offset : 3367
value : 4294967295
[M] file:#ib_redo40 gblockno:1317305 lsn:674459780 cblockno:6732 offset:132 name:MLOG_2BYTES(2)
spaceno : 4294967278
pageno : 293
offset : 3371
value : 0
[M] file:#ib_redo40 gblockno:1317305 lsn:674459788 cblockno:6732 offset:140 name:MLOG_4BYTES(4)
spaceno : 4294967278
pageno : 34
offset : 46
value : 1
[M] file:#ib_redo40 gblockno:1317305 lsn:674459795 cblockno:6732 offset:147 name:MLOG_8BYTES(8)
spaceno : 4294967278
pageno : 34
offset : 4168
value : 16716
[M] file:#ib_redo40 gblockno:1317305 lsn:674459806 cblockno:6732 offset:158 name:MLOG_8BYTES(8)
spaceno : 4294967278
pageno : 293
offset : 3335
value : 16716
[M] file:#ib_redo40 gblockno:1317305 lsn:674459818 cblockno:6732 offset:170 name:MLOG_2BYTES(2)
spaceno : 4294967278
pageno : 293
offset : 3343
value : 0
[M] file:#ib_redo40 gblockno:1317305 lsn:674459826 cblockno:6732 offset:178 name:MLOG_1BYTE(1)
spaceno : 4294967278
pageno : 293
offset : 3513
value : 1
[M] file:#ib_redo40 gblockno:1317305 lsn:674459834 cblockno:6732 offset:186 name:MLOG_WRITE_STRING(30)
spaceno : 4294967278
pageno : 293
offset : 3514
length : 64
data : b'de98b78e-dbb5-11f0-9e1b-000c2980c11e:379\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
[M] file:#ib_redo40 gblockno:1317305 lsn:674459907 cblockno:6732 offset:259 name:MLOG_1BYTE(1)
spaceno : 4294967278
pageno : 293
offset : 3347
value : 3
[M] file:#ib_redo40 gblockno:1317305 lsn:674459915 cblockno:6732 offset:267 name:MLOG_MULTI_REC_END(31)
spaceno : -1
pageno : -1
- [M] file:#ib_redo40 gblockno:1317305 lsn:674459916 cblockno:6732 offset:268 name:MLOG_UNDO_HDR_REUSE(24)
spaceno : 4294967279
pageno : 147
trx_id : 16717
[M] file:#ib_redo40 gblockno:1317305 lsn:674459926 cblockno:6732 offset:278 name:MLOG_2BYTES(2)
spaceno : 4294967279
pageno : 147
offset : 40
value : 272
[M] file:#ib_redo40 gblockno:1317305 lsn:674459935 cblockno:6732 offset:287 name:MLOG_2BYTES(2)
spaceno : 4294967279
pageno : 147
offset : 42
value : 272
[M] file:#ib_redo40 gblockno:1317305 lsn:674459944 cblockno:6732 offset:296 name:MLOG_2BYTES(2)
spaceno : 4294967279
pageno : 147
offset : 104
value : 272
[M] file:#ib_redo40 gblockno:1317305 lsn:674459953 cblockno:6732 offset:305 name:MLOG_MULTI_REC_END(31)
spaceno : -1
pageno : -1
- [S] file:#ib_redo40 gblockno:1317305 lsn:674459954 cblockno:6732 offset:306 name:MLOG_UNDO_INSERT(20)
spaceno : 4294967279
pageno : 147
len : 50
data : b'\x0b\x00\x84\x1a$de98b78e-dbb5-11f0-9e1b-000c2980c11e\x08\x80\x00\x00\x00\x00\x00\x01{'
- [S] file:#ib_redo40 gblockno:1317305 lsn:674460011 cblockno:6732 offset:363 name:MLOG_REC_INSERT(67)
spaceno : 4294967294
pageno : 590
index_version: 1
index_flags: 1
cols : 5
inst_cols : None
n_uniq : 2
index_fields: [32768, 32776, 32774, 32775, 32776]
index_versioned_fields: None
offset : 2540
end_seg_len: 71
info_and_status_bits: -1
origin_offset: -1
mismatch_index: -1
data : b'$\x00\x01H\xf5!de98b78e-dbb5-11f0-9e1b-000c2980c11e\x80\x00\x00\x00\x00\x00\x01{\x00\x00\x00\x00AM\x81\x00\x00\x00\x93\x01\x10\x80\x00\x00\x00\x00\x00\x01{'
- [S] file:#ib_redo40 gblockno:1317305 lsn:674460107 cblockno:6732 offset:459 name:MLOG_2BYTES(2)
spaceno : 4294967279
pageno : 147
offset : 56
value : 2
- [S] file:#ib_redo40 gblockno:1317305 lsn:674460115 cblockno:6732 offset:467 name:MLOG_8BYTES(8)
spaceno : 0
pageno : 5
offset : 15908
value : 16717
- [S] file:#ib_redo40 gblockno:1317305 lsn:674460125 cblockno:6732 offset:477 name:MLOG_8BYTES(8)
spaceno : 0
pageno : 5
offset : 15908
value : 16718输出这么大一堆, 其实关键的就是:
- [S] file:#ib_redo40 gblockno:1317304 lsn:674459421 cblockno:6731 offset:285 name:MLOG_REC_INSERT(67)
spaceno : 177
pageno : 4
index_version: 1
index_flags: 1
cols : 4
inst_cols : None
n_uniq : 1
index_fields: [32774, 32774, 32775, 4]
index_versioned_fields: None
offset : 99
end_seg_len: 29
info_and_status_bits: 0
origin_offset: 6
mismatch_index: 0
data : b'\x00\x00\x00\x10\xff\xf2\x00\x00\x00\x000\x00\x00\x00\x00\x00AK\x82\x00\x00\x00\x92\x01\x10\x80\x00\x00\x01'这个data就是实际insert的内容, 一共29字节,
null_bitmask 1 字节
record_header: 5 字节
rowid: 6字节
trxid+rollptr: 13字节
id: 4字节
我们也可以打开ibd文件确认确认:

其它SQL对应的redo信息就请自己去解析吧, 这玩意内容太多了. 还可以在人工去看对应的表空间的对应page验证
由于输出内容太多, 所以我们还加了个过滤功能: 比如查看指定的mlog.
比如
有2种方法:
方法1:
python3 redo_reader.py /data/mysql_8037/mysqllog/redolog/#innodb_redo/#ib_redo40 --start-lsn 674460189 --set name=MLOG_UNDO_INSERT | strings |grep ';space_id'
看到的id=398就是被truncate表的indexid.有了这玩意我们就能使用ibd2sql去扫描磁盘恢复被truncate表了.
方法2:
python3 redo_reader.py /data/mysql_8037/mysqllog/redolog/#innodb_redo/#ib_redo40 --start-lsn 674460189 --set name=MLOG_REC_UPDATE_IN_PLACE | strings |grep ';space_id' -B 17
这个id=399是新的indexid,旧的我们得根据roll_ptr去undo里面找:
python3 undo_reader.py /data/mysql_8037/mysqldata/undo_001 -r 281475001813712
这里解析出来的id=398就是我们truncate表的indexid了(删除之前忘记查看原来是多少了....)
mysql的redo记录的是物理+逻辑日志.结构比较简单(复杂了性能就是个问题), 我们能解析mysql redo文件了, 能了解到各SQL对应的具体的内部操作了, 其实再进一步就能做个类似xbk的热备工具了(-_-)
我们这里只解析了2条SQL, 其它SQL对应的redo操作请读者自行验证.
参考:
https://dev.mysql.com/doc/refman/8.0/en/innodb-init-startup-configuration.html
https://dev.mysql.com/doc/dev/mysql-server/latest/PAGE_INNODB_REDO_LOG_FORMAT.html
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。