背景
生产环境 MySQL 运行在 Docker 中,数据目录通过卷挂载到宿主机(例如/moli/data/mysql_prod_data)。在异常关机或磁盘异常后,容器反复重启失败。错误日志中可能出现:
-`[MY-012671][InnoDB]Encryption algorithm support missing: N`-`[MY-013183]InnoDB Assertion failure: log0recv.cc:3388: err==DB_SUCCESS`- 调用栈位于`recv_recovery_from_checkpoint_start`(redo 恢复阶段) -`/usr/sbin/mysqld(ut_dbg_assertion_failed(char const*, char const*, unsigned long)+0x2cc)[0x57dfa60de03c]/usr/sbin/mysqld(+0x1dfb778)[0x57dfa5f96778]/usr/sbin/mysqld(dd::bootstrap::DDSE_dict_init(THD*, dict_init_mode_t, unsigned int)+0x88)[0x57dfa5dcd088]/usr/sbin/mysqld(dd::upgrade_57::do_pre_checks_and_initialize_dd(THD*)+0x2cf)[0x57dfa5dc399f]/usr/sbin/mysqld(+0xf5a5e1)[0x57dfa50f55e1]/usr/sbin/mysqld(+0x216a83f)[0x57dfa630583f]/lib/x86_64-linux-gnu/libpthread.so.0(+0x74a4)[0x75de520a64a4]/lib/x86_64-linux-gnu/libc.so.6(clone+0x3f)[0x75de503f5d0f]Trying to get some variables. Some pointers may be invalid and cause the dump to abort. Query(0): Connection ID(thread ID):1Status: NOT_KILLED The manual page at http://dev.mysql.com/doc/mysql/en/crashing.html contains information that shouldhelpyoufindout what is causing the crash.`本文梳理原因判断、备份、强制恢复、导出、更换数据目录、导入的完整流程,便于复现与内网 Runbook 化。
现象与日志含义
- 崩溃位置在 InnoDB从 redo 做恢复阶段,而非普通连接或查询。
Encryption algorithm support missing常见关联包括:- 数据目录中存在表空间加密 / keyring相关元数据,但当前实例未正确加载密钥或插件;
- 或使用过的MySQL 构建/镜像与当前不一致;
- 若从未启用加密,redo 或页损坏也可能被误解析,表现类似。
- 若栈中出现
upgrade_57等字样,说明启动路径涉及5.7 → 8.0升级检查;抢救阶段应尽量保持与原小版本一致,避免随意换大版本「试运气」。
卷挂载本身通常不是根因:卷只是把同一套数据文件交给mysqld;问题在于数据与进程/配置是否匹配以及文件是否损坏。
宿主机侧:数据目录属主
若文件管理器中数据目录下文件属主为dnsmasq等与容器内MySQL 运行用户不一致的账号,可能导致读写异常。官方mysql镜像内mysql用户常见为UID/GID 999(以实际镜像为准):
dockerrun--rmmysql:8.0.15idmysql在已完成整目录备份的前提下:
sudochown-R999:999 /moli/data/mysql_prod_data修正后再次启动。此操作不能保证解决加密/redo 断言,但成本低,应优先执行。
第一步:备份(任何操作之前)
整目录物理备份(实例无法启动时尤为重要)
- 停止容器,避免拷贝过程中文件仍在变化。
- 使用保留权限的拷贝:
dockercompose stop mysqlsudocp-a/moli/data/mysql_prod_data /moli/backup/mysql_prod_data_$(date+%Y%m%d_%H%M%S)备份应存放在与数据盘不同的磁盘或机器。
逻辑备份(mysqldump)输出位置
dockerexec<容器名>mysqldump...>备份.sql重定向>由宿主机 shell 处理,因此.sql文件位于执行命令时终端的当前工作目录(或所指定的绝对路径),不在容器内部。
第二步:innodb_force_recovery尝试启动
在[mysqld]或 Dockercommand中配置(从 1 开始逐级增大,直至 6):
innodb_force_recovery = 1示例(Composecommand中):
-"--innodb_force_recovery=6"说明:
- 数值越大,越可能丢失数据,越接近只读抢救场景。
- 不可长期在生产环境以高档位运行;目标是能够连接并导出数据。
- 若低档位无法启动而高档位可以,说明 redo/字典路径上存在严重不一致或损坏,更需在导出后换新数据目录重建实例。
第三步:逻辑导出(按需库表)
示例:仅导出业务库moli_client_prod(密码含特殊字符时用单引号包裹):
dockerexecmoli-mysql-prod mysqldump-uroot-p'你的密码'\--databasesmoli_client_prod\--single-transaction\--routines--events--triggers\>moli_client_prod_$(date+%Y%m%d_%H%M%S).sql--databases会在 dump 中包含CREATE DATABASE/USE,便于导入到全新空实例。mysqldump: [Warning] Using a password on the command line can be insecure为预期安全提示,不表示导出失败。- 根据导出文件体积(如数 GB)可粗判是否为空文件或严重不完整。
- 数值越大,越可能丢失数据,越接近只读抢救场景。
- 不可长期在生产环境以高档位运行;目标是能够连接并导出数据。
- 若低档位无法启动而高档位可以,说明 redo/字典路径上存在严重不一致或损坏,更需在导出后换新数据目录重建实例。
innodb_force_recovery各档位含义(1~6)
0为默认值,表示不启用强制恢复。1~6为 MySQL 官方定义的抢救模式,语义与 MySQL 手册 · InnoDB 强制恢复 一致;数字越大,对正常一致性假设破坏越大,数据越不可信。
| 档位 | 含义(概要) |
|---|---|
1(SRV_FORCE_IGNORE_CORRUPT) | 遇到损坏页时尽量不因此让服务器崩溃;mysqldump导出时可能跳过损坏的二级索引记录。仍可能在某些 DML 上因损坏页失败。 |
2(SRV_FORCE_NO_BACKGROUND) | 不启动主线程中的后台任务(如purge等),避免后台操作触碰坏页或加剧不一致。 |
3(SRV_FORCE_NO_TRX_UNDO) | 崩溃恢复完成后不执行事务回滚(不做 undo 驱动的回滚)。未提交事务可能被当成已生效,逻辑上更危险。 |
4(SRV_FORCE_NO_IBUF_MERGE) | 不执行 change buffer(插入缓冲)合并。避免因合并读写坏页导致启动失败;二级索引可能与主键索引暂时不一致。 |
5(SRV_FORCE_NO_UNDO_LOG_SCAN) | 启动时不扫描 undo 日志;InnoDB 将未结束的事务视为已提交。极易造成脏读进库表,仅作最后抢救。 |
6(SRV_FORCE_NO_LOG_REDO) | 不做 redo 日志的前滚(roll-forward),即跳过基于 redo 的恢复阶段。能绕过部分 redo/恢复路径上的断言或损坏,但可能丢失崩溃前已提交却未反映到数据文件中的修改,且数据文件与日志可能严重不一致。 |
操作上的共识(与手册一致):
- 任意
innodb_force_recovery > 0时,应视为应急模式:除导出数据、DROP表等必要操作外,不要对 InnoDB 表做正常业务写入。 - 手册指出,≥ 4时 InnoDB 对数据采取更保守策略,实践中常将实例当作只读抢救使用。
- 成功启动后应尽快
mysqldump(或等价逻辑备份),并在新的空数据目录上初始化实例、导入备份,禁止长期带着强制恢复参数跑生产。
第四步:更换数据目录并正常启动(移除强制恢复)
- 停止容器。
- 重命名旧数据目录作为归档,新建空目录,并将属主设为容器内
mysql的 UID:GID。 - 从 Compose 与
conf.d中删除所有innodb_force_recovery相关配置。 - 启动容器,使 MySQL 在空目录上完成初始化。
sudomv/moli/data/mysql_prod_data /moli/data/mysql_prod_data_bad_$(date+%Y%m%d)sudomkdir-p/moli/data/mysql_prod_datasudochown-R999:999 /moli/data/mysql_prod_datadockercompose up-dmysql(999:999请按id mysql实际输出调整。)
第五步:导入 dump
dockerexec-imoli-mysql-prod mysql-uroot-p'你的密码'\</path/to/moli_client_prod_时间戳.sql大文件导入耗时较长属正常现象。导入后执行:
SHOW TABLES、关键表COUNT(*)- 应用连接数据库端口做冒烟测试
Compose 与配置方面的长期建议
| 项 | 建议 |
|---|---|
privileged: true | 对 InnoDB 恢复无必要,稳定后建议关闭以降低风险面。 |
MYSQL_USER默认为root | 易与官方镜像初始化语义冲突;更稳妥为仅设置MYSQL_ROOT_PASSWORD,业务用户使用独立账号创建。 |
expire_logs_days | MySQL 8.0 已弃用,可改为binlog_expire_logs_seconds。 |
| 镜像版本 | 数据健康后,可在充分验证的前提下将8.0.15等旧补丁升级到较新8.0.x。 |
复盘与预防
| 项 | 说明 |
|---|---|
| 抢救数据完整性 | 使用innodb_force_recovery=6导出的数据不保证 100% 一致,需业务侧抽查;若存在更早全量备份 + binlog,可对比或做时间点恢复补数据。 |
| 持久化参数 | innodb_flush_log_at_trx_commit=2与较大的sync_binlog在崩溃时可能丢失最近少量已提交事务,需在性能与可靠性间权衡。 |
innodb_flush_method=O_DIRECT | 部分网络存储或特殊挂载下可能出现兼容问题;若遇疑难 I/O 现象,可在测试环境对比fsync等行为。 |
| 例行备份 | 定期全备(逻辑或物理)+ 保留 binlog比依赖强制恢复可靠得多。 |
小结
推荐处理顺序为:
整目录备份 → 修正数据目录属主(如需要)→ 逐级尝试innodb_force_recovery→ 导出所需库 → 空目录初始化新实例并移除强制恢复 → 导入 SQL → 验证。
原则:对数据目录的删改与 redo 相关操作必须建立在完整备份之上;强制恢复仅为临时过桥,生产应以干净实例 + 逻辑恢复为最终形态。