如何创建可被新版MariaDB使用的数据库文件系统快照?
背景
我有一个大型数据库,使用mysqldump备份耗时极长,目前采用以下脚本通过ZFS快照做增量备份:
#!/usr/bin/env bash mysql -u root << EOF FLUSH TABLES WITH READ LOCK; system zfs snapshot db01/mysql@cron-$(date +%Y%m%d.%H%M); UNLOCK TABLES; EOF
将快照恢复到同CentOS7镜像、同版本MariaDB的测试实例时可正常启动,但迁移至装有新版MariaDB的系统时启动失败,报错信息如下:
Nov 24 02:55:51 test001 mariadbd[453773]: 2022-11-24 2:55:51 0 [ERROR] InnoDB: Upgrade after a crash is not supported. This redo log was created before MariaDB 10.2.2. Nov 24 02:55:51 test001 mariadbd[453773]: 2022-11-24 2:55:51 0 [ERROR] InnoDB: Plugin initialization aborted with error Generic error Nov 24 02:55:51 test001 mariadbd[453773]: 2022-11-24 2:55:51 0 [Note] InnoDB: Starting shutdown...
根据官方说明,FLUSH TABLES WITH READ LOCK会标记所有表为“已正常关闭”,但为何新版MariaDB会认为是崩溃后升级?如何优化快照使其兼容新版MariaDB?或有无其他简便方法实现需求?我曾尝试使用FLUSH TABLES <list of all tables> FOR EXPORT,但在MariaDB 5.5.56版本中无效。
问题原因
FLUSH TABLES WITH READ LOCK (FTWRL)确实会锁定所有表并刷新脏页到磁盘,但它并没有完全关闭InnoDB的redo日志循环。老版本MariaDB(如5.5)的InnoDB在FTWRL后,redo日志依然处于“活跃”状态——虽然没有新的写入,但日志头部的状态标记并未设置为“正常关闭”。当新版本MariaDB读取这种快照时,会触发InnoDB的安全校验逻辑(10.2.2之后的版本对跨版本升级的崩溃场景做了严格限制),误判为数据库崩溃后直接升级。
优化快照兼容新版本的方案
1. 补充InnoDB干净关闭步骤(推荐)
在FTWRL之后,手动触发InnoDB的完全关闭,确保redo日志标记为正常状态后再做快照:
#!/usr/bin/env bash mysql -u root << EOF FLUSH TABLES WITH READ LOCK; SET GLOBAL innodb_fast_shutdown = 0; # 执行全量脏页刷新和日志清理 system service mariadb stop; system zfs snapshot db01/mysql@cron-$(date +%Y%m%d.%H%M); system service mariadb start; UNLOCK TABLES; EOF
- 说明:
innodb_fast_shutdown=0会让InnoDB完成所有未完成的事务、刷新所有脏页并清理redo日志,确保数据文件和日志处于完全一致的干净状态。 - 注意:此方案会短暂停库,适合能接受短时间停机的场景。
2. 临时跳过InnoDB崩溃校验(应急用,不推荐)
如果无法停库,可在新版本MariaDB的my.cnf中添加临时参数:
[mysqld] innodb_force_recovery = 1
启动数据库后,立即执行ALTER TABLE ... FORCE重建所有表,完成后移除该参数并重启。这种方法存在数据损坏风险,仅作为临时应急手段。
3. 先升级原库再做快照
如果业务允许,先将原MariaDB 5.5升级到目标新版本(如先升级至10.5),再生成ZFS快照。升级后的快照天然兼容新版本,避免跨版本校验问题。升级前务必做好全量备份。
替代备份方案
1. 使用MariaDB热增量备份工具
官方的mariabackup(Percona XtraBackup的分支)支持热增量备份,无需停库,备份文件天然兼容跨版本升级——只需先在原版本做全备,再用新版本工具恢复并执行升级操作。
2. ZFS快照+逻辑备份结合
保留现有ZFS快照作为快速恢复手段,同时定期用mysqldump分库分表导出全量逻辑备份(减少单文件导出耗时)。迁移时先用快照恢复到同版本实例,再导出逻辑备份导入新版本。
内容的提问来源于stack exchange,提问作者bexamous

