场景:Docker Swarm 集群,MariaDB 10.4 服务异常重启,启动失败;数据库日志发现残留 XA Prepared 事务,常规重启无法自动恢复,业务无法连接数据库。 环境:Docker Swarm + MariaDB 10.4,业务使用XA分布式事务。
一、故障现象
-
MariaDB service 在 Swarm 集群反复重启,容器不断 crash,业务无法访问数据库;
-
查看容器日志,数据库启动阶段卡在事务恢复流程,无法完成启动;
-
多次重启 Swarm service 无效,数据卷 PVC(Swarm volume)数据保留完好,不能直接删除 volume。
mysql容易的异常日志:

二、排障思路(完整定位过程)
核心原则:不直接在故障业务容器内操作数据,防止数据损坏;使用同版本临时容器挂载原数据卷做离线修复
- 查看 Swarm service 容器日志,初步判断是数据库内部事务恢复问题,不是 Swarm 网络、端口或资源限制问题;
- 进入故障容器查看 mysqld 内核日志,发现关键日志:
Found prepared XA transactions,确认存在XA悬停事务;
直接进容器看到的异常日志,一直不能明确定位到具体原因。排查服务器磁盘空间都是够用的。
容器内手动前台启动 mysqld(最关键,直接打印崩溃原因)
# 容器内执行
mysqld --datadir=/config/databases

-
原理回顾:XA 两阶段事务,prepare 成功之后,协调器宕机/网络中断,没有收到 commit/rollback。数据库重启时会尝试自动处理;协调器永久丢失时,自动恢复失败,数据库无法启动;而且容器内的mysql进程无法杀除并重启,因为mysql的容器镜像中加了mysqld-safe的异常重启服务。
-
方案:新建临时容器,挂载原有 MariaDB 数据 volume,手动启动 mysqld,完成XA事务检查与清理;
4.1 踩坑:临时容器缺少 /var/run/mysqld 目录,mysqld 默认把 sock、pid 文件写入该路径,导致启动失败;修改启动参数,将 sock/pid 文件放到 /tmp 目录解决;
docker run --rm -it \
--entrypoint bash \
-v /root/controller_deployer/data/mysql:/config \
-e PUID=0 -e PGID=0 \
registry.tethrnet.com:5000/mariadb:version-110.4.15mariabionic
--entrypoint bash 是关键!
- 不再执行镜像默认的 mariadb 启动脚本
- 容器启动直接进入 bash,不会自动启动 mysqld_safe
4.2 数据库成功拉起后,登录验证XA事务;MariaDB 10.4 没有 information_schema.innodb_prepared_transactions 系统表,使用 XA RECOVER; 命令检查;
临时容器内执行恢复命令
mysqld --datadir=/config/databases --tc-heuristic-recover=ROLLBACK
此时前台启动,执行 XA 事务恢复。
- 看到
ready for connections就代表恢复成功。 - 保持这个终端,新开一个临时容器终端进去验证 XA 事务。
4.3 确认XA残留事务清理完成,优雅关闭临时数据库,重新启动 Swarm 的 MariaDB 业务服务,故障恢复。
执行查询,确认 XA 事务:
SELECT * FROM information_schema.innodb_prepared_transactions;
重要提醒
- 以后绝对不要再带
--tc-heuristic-recover参数启动,这个参数是一次性修复参数;重复带参数启动会直接 abort。 - 启动业务容器前,留意宿主机数据目录权限,避免再次出现 Permission denied。
三、完整操作步骤
3.1 备份数据卷(操作前必做)
修复前先备份 volume,防止误操作丢失数据
# 将你的mariadb数据卷做备份,替换 volume 名称
docker run --rm -v mariadb-data:/source -v /host/backup:/dest alpine cp -r /source /dest
3.2 启动临时容器挂载原数据卷
使用和业务完全一致的 MariaDB 版本,保证数据文件兼容
docker run -it --rm \
-v mariadb-data:/config/databases \
--entrypoint bash mariadb:10.4.15
进入容器后,
/config/databases就是原数据库的数据目录。
3.3 启动 mysqld,指定参数规避 sock/pid 目录缺失问题
mysqld --datadir=/config/databases \
--skip-networking=0 \
--socket=/tmp/mysqld.sock \
--pid-file=/tmp/mysqld.pid
等待日志输出 ready for connections,代表数据库内核成功启动。
注意:这个终端保持不动,mysqld 在此前台运行。
3.4 新开宿主机终端,进入临时容器登录数据库
# 查看临时容器ID,替换为你的容器ID
docker exec -it 982c15977c7a bash
# 使用自定义sock文件登录
mysql -uroot -S /tmp/mysqld.sock
3.5 检查XA事务(重点踩坑点)
❌ 错误命令(MySQL8专属,MariaDB 10.4不存在这张表)
select * from information_schema.innodb_prepared_transactions;
✅ MariaDB 正确查看XA pending事务命令
XA RECOVER;
- 返回空结果:✅ 残留XA事务已经处理完成;
- 如有返回记录:可以手动执行
XA ROLLBACK 'gid';逐个回滚。
3.6 验证库表可用性
show databases;
确认业务库都正常加载,表可以正常访问。
3.7 关闭临时库,恢复业务服务
-
在 mysqld 运行窗口按
Ctrl+C,优雅关闭数据库进程; -
退出临时容器,容器会自动销毁;
-
回到 Docker Swarm,重启 mariadb service:
docker service update --force mariadb-service
-
观察容器状态,容器正常启动不再反复crash,业务连接恢复。
四、原理分析
XA分布式事务分为2PC:
- Prepare:各资源节点持久化事务,返回成功;
- Commit:事务协调器通知所有节点提交。
故障场景:业务XA事务执行到prepare成功,协调器节点崩溃/网络断开,没有下发commit/rollback指令。 数据库重启时,InnoDB引擎会扫描所有prepared状态XA事务,等待协调器指令。协调器永久丢失时,数据库无法自动完成事务处理,数据库启动阻塞。
⚠️ 重要提醒:启发式回滚属于应急修复手段。修复完成后,业务侧需要核对相关业务数据,校验数据一致性。
五、本次故障踩坑清单
- 混淆 MySQL8 和 MariaDB 的系统表,MariaDB 10.4 没有 innodb_prepared_transactions,查询会报表不存在;
- 临时容器环境缺少
/var/run/mysqld目录,mysqld 启动时创建pid、socket文件失败,数据库内核已经加载完成但服务退出; - 不要直接在业务Swarm容器内直接修改、启动mysqld,容易引发数据损坏;优先临时容器挂载volume离线修复;
- Docker Swarm 服务会自动重启崩溃容器,反复重启会加剧事务恢复异常,故障期间建议先缩容副本为0,避免持续损坏数据。
六、预防方案
- XA事务协调器做好状态持久化,记录每个XA事务全局ID和状态,协调器重启后可以继续处理残留事务;
- 增加监控:定时执行
XA RECOVER;,一旦检测到pending事务,触发告警,尽早发现; - Swarm中数据库服务配置合理的重启策略,故障时不要无限快速重启,避免放大故障;
- 定期备份volume,数据库数据卷损坏时可以快速回滚;