导读
上一篇文章介绍了 并发导入, 但还有一种需求是 只恢复特定的某张表. 比如这张表有坏块啊, 或者其它啥需求, 反正就是要恢复这张表, 但是又没单独备份这张表.
只有 一个定时全备(mysqldump
)
那要怎么恢复呢???
分析
上一篇介绍的 mysqldump拆分脚本https://www.modb.pro/doc/125805 还支持 仅拆分出来指定的表, 然后我们再从binlog中解析出指定的表做恢复即可.
也就是说现在 只要从binlog中提取指定的表即可. 但客户环境可能不允许使用 binlog2sql
或者my2sql
等工具…
没事, binlog文件结构不复杂, 自己写个简单脚本提取指定的Binlog即可. 好在之前有解析过binlog文件: https://www.modb.pro/db/625148
binlog文件提取指定的表
我们指定binlog是由 很多event组成的, 而一个事务由 GTID_EVENT
和众多 ROW_EVENT
再加上XID_EVENT
组成, 而ROW_EVENT
又细分为 DELETE_EVENT
, WRITE_EVENT
, UPDATE_EVENT
. 每个ROW_EVENT
还有个TABLE_MAP_EVENT
来记录元数据信息, 比如字段类型, 表名等信息 (不包含字段名字)
所以我们只需要解析出这几个EVENT即可. 再加上第一个EVENT描述信息即可.
每个EVENT都由 HEADER
和 PAYLOAD
组成. HEADER结构如下:
对象 | 大小(bytes) | 描述 |
---|---|---|
timestamp | 4 | event生成时间 |
event_type | 1 | event类型 |
server_id | 4 | 产生这个event的server_id |
event_size | 4 | event大小(含header的19bytes) |
log_pos | 4 | event的结束位置(pos) |
flags | 2 | flags |
而PAYLOAD
的话, 每个EVENT都不一样, 这里只看 TABLE_MAP_EVENT
的一部分, 因为本次解析binlog只涉及到这一丢丢
名字 | 大小(字节) | 描述 |
---|---|---|
table_id | int | 表打开的id, 不是数据库里面的table_id |
flags | 2 | 保留字段 |
database_name_length | 可变长度 | 数据库名长度 |
database_name | 取决于database_name_length | 数据库名(以0x00结尾, 这个字节不计算在database_name_length中) |
table_name_length | 可变长度 | 表名长度 |
table_name | 取决于table_name_length | 表名(以额外的0x00结尾, 就是不在table_name_length的计算中) |
column_count | 可变长度 | 多少个字段 |
column_type_list | 取决于column_count | list类型, 每个字段的数据类型,用1字节表示(比如3表示int 详情) |
… | … | 暂时用不上其它的 |
脚本编写思路
简单说一下脚本编写思路, 毕竟每个人的思路都不一样
读取BINLOG文件
如果是 table_map_event
就解析payload, 得到库表名字, 并保存下来
如果 匹配表名成功 则记录该EVENT
如果是 row_event
则匹配刚才的库表信息, 成功则记录下来
如果是GTID_EVENT
直接记录下来, 这是事务的开头
如果是 XID_EVENT
记录下来
判断 记录的event队列长度, 大于等于4 则表示 又成功匹配上的表 则写入新文件
清空event队列
其它EVENT 则跳过(seek)
思路还是比较简单的. 就是匹配.
测试
从mysqldump中拆分出指定的表
使用--database
和--table
匹配需要的表名信息
python MysqlDumpSplitSQL.py t20240228_alldb.sql --database ibd2sql --table ddcw_alltype_table
然后导入数据库
mysql -h127.0.0.1 -P3314 -p123456 < /root/mysqldump_t20240226/splitByddcw_20240301_084906/dbs/ibd2sql/ddcw_alltype_table.sql
从Binlog提取指定的表
用法和上一个脚本一样
使用--database
和--table
匹配需要的表名信息
python binlogFtable.py /data/mysql_3314/mysqllog/binlog/m3314.000002 --database ibd2sql --table ddcw_alltype_table
然后导入数据库, 由于带有gtid
, 故直接导入数据库会失败, 我们需要使用--skip-gtids
来忽略gtid信息
mysqlbinlog --skip-gtids /root/mysqldump_t20240226/BinlogFtableByddcw_20240301_085208/m3314.000002 | mysql -h127.0.0.1 -P3314 -p123456
验证
发现我们解析出来的哪张表数据确实是执行binlog之后的了, 证明这个方法是可行的. (我这里只有一个delete操作, 是为了方面演示, 实际环境可能是一大堆DML操作)
总结
本次 通过拆分 mysqldump导出的数据, 然后提取binlog指定的表, 最后使用mysqlbinlog来解析binlog并导入数据库 来实现单表恢复. 看起来流程还是很麻烦的. 但原理还是简单, 就是匹配指定的表, 然后重新回放.
当然如果又备库的话, 直接从备库导出更方便.
附脚本
mysqldump拆分脚本: https://www.modb.pro/doc/125805
binlog提取指定表脚本:https://www.modb.pro/doc/125819