背景
环境: mysql 8.0
不小心删除了mysql数据目录, 但还剩个.ibd文件在. 没得备份, 没得binlog , 要恢复这个ibd文件里面的数据.
啊. 这… 先打一顿没有做备份的dba
分析
我们通常是使用备份+binlog来恢复数据, 但这次只有个孤零零的ibd文件.
我们知道mysql 8.0 的ibd文件也包含元数据信息(你问我怎么知道的?). 所以我们先恢复表结构, 再恢复数据.
恢复表结构
如果开发有相关的DDL更好. 没得的话. 我们就自己解析.
mysql 8.0的ibd文件存在sdi page 记录元数据信息的, 压缩的json格式. 我们可以使用官方自带的 ibd2sdi 解析出来这个json信息.
ibd2sdi /data/mysql_3314/mysqldata/ibd2sql/ddcw_alltype_table.ibd
但还要自己去拼接DDL, 太麻烦了.
所以我们使用其它工具来提取DDL, 这里就使用 ibd2sql 来提取(为啥使用这个呢? 因为是我写的 -_- 用起来顺手)
python3 main.py /tmp/ddcw_alltype_table.ibd --ddl
现在DDL已经提取出来了. 我们先创建个测试环境, 创建相同的库, 再使用上面的DDL去恢复相关的表结构
CREATE TABLE IF NOT EXISTS `ibd2sql`.`ddcw_alltype_table`(
`id` int NOT NULL AUTO_INCREMENT,
`int_col` int NULL,
`tinyint_col` tinyint NULL DEFAULT '1',
`smallint_col` smallint NULL,
`mediumint_col` mediumint NULL,
`bigint_col` bigint NULL,
`float_col` float NULL,
`double_col` double NULL,
`decimal_col` decimal(10,2) NULL,
`date_col` date NULL,
`datetime_col` datetime NULL,
`timestamp_col` timestamp NULL,
`time_col` time NULL,
`year_col` year NULL,
`char_col` char(100) NULL,
`varchar_col` varchar(200) NULL DEFAULT 'aa',
`binary_col` binary(10) NULL,
`varbinary_col` varbinary(20) NULL,
`bit_col` bit(4) NULL,
`enum_col` enum('A','B','C') NULL,
`set_col` set('X','Y','Z') NULL,
`josn_type` json NULL,
`newcol` varchar(200) NULL DEFAULT 'aa',
`newcol2` varchar(200) NULL DEFAULT 'aa',
`newcoldasdas2` varchar(300) NULL DEFAULT 'bbaa',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ;
恢复数据
恢复了表结构后, 就该恢复数据了.
方法1(推荐)
mysql可以使用discard table来删除表空间, 然后使用import tablespace 来导入表空间. 我们就使用这种方式来恢复数据.
官网介绍: https://dev.mysql.com/doc/refman/8.0/en/innodb-table-import.html
-- 移除表空间
alter table ddcw_alltype_table discard tablespace;
-- 拷贝要恢复的表空间
system cp -ra /tmp/ddcw_alltype_table.ibd /data/mysql_3314/mysqldata/ibd2sql
-- 导入要恢复的表空间
alter table ddcw_alltype_table import tablespace;
-- 验证数据
select count(*) from ddcw_alltype_table;
这种方法非常简单方便, 推荐使用.
但有可能会失败(人生不总是一帆风顺).
ERROR 1815 (HY000): Internal error: Cannot reset LSNs in table `dbname`.`tablename` : Data structure corruption
方法2
如果无法导入表空间的话, 我们还可以解析这个ibd文件得到相关的数据. 这种工具市面上较少 我这里还是使用 ibd2sql (夹带私货-_-)
目前不支持溢出页(默认置为NULL). 也不建议在mysql里面存储大数据.
数据类型基本上都支持, 不支持空间坐标字段(为啥? 因为要太复杂了, 一时半会解析不了…)
python3 main.py /tmp/ddcw_alltype_table.ibd --sql > /tmp/ddcw_alltype_table.sql
然后我们就可以把解析出来的sql导入数据库了. 当然也可以解析的时候直接通过管道写入数据库
mysql -h127.0.0.1 -P3314 -p123456 < /tmp/ddcw_alltype_table.sql
总结
虽然只剩一个ibd文件也能恢复数据, 但还是要备份. 毕竟这些工具不一定能用. (多数都有备份, 这些工具使用例子就少, 支持范围就小)
如果ibd2sql工具使用有啥问题, 请到github提相关issue, 附上ibd2sql版本和mysql版本, 最好能附上debug日志.
下载地址: https://github.com/ddcw/ibd2sql/archive/refs/tags/v1.1.tar.gz
单表恢复方案参考:
- pxb+binlog (binlog提取单表 看之前的文章)
- mysqldump+binlog
- 从库拷贝ibd文件
- innodb_force_recovery + mysqldump
- ibd2sql
附mysql数据类型
(懒得整理, 直接拿ibd2sql里面的代码来看吧, 将就)
innodb_read_data object:
read_varchar
read_char
read_varbinary
read_binary
read_bit
read_tinyint
read_smallint
read_mediumint
read_int
read_bigint
read_float
read_double
read_decimal #最离谱...
read_date
read_datetime
read_time
read_timestamp
read_year
read_enum
read_set
# json (可能存在溢出页)
read_json
# 大字段 (可能存在溢出页)
read_tinyblob
read_mediumblob
read_blob
read_longblob
read_tinytext
read_mediumtext
read_text
read_longtext
# 空间坐标(也NM离谱)
read_geometry #几何 (通用)
read_point #点
read_linestring #线
read_polygon #多边形
read_multipoint
read_multilinestring
read_multipolygon
read_geometrycollection #几何集合