Docker Swarm 线上环境 MariaDB XA 悬停事务故障排查

场景:Docker Swarm 集群,MariaDB 10.4 服务异常重启,启动失败;数据库日志发现残留 XA Prepared 事务,常规重启无法自动恢复,业务无法连接数据库。 环境:Docker Swarm + MariaDB 10.4,业务使用XA分布式事务。

一、故障现象

  1. MariaDB service 在 Swarm 集群反复重启,容器不断 crash,业务无法访问数据库;

  2. 查看容器日志,数据库启动阶段卡在事务恢复流程,无法完成启动;

  3. 多次重启 Swarm service 无效,数据卷 PVC(Swarm volume)数据保留完好,不能直接删除 volume。

    mysql容易的异常日志:

image

二、排障思路(完整定位过程)

核心原则:不直接在故障业务容器内操作数据,防止数据损坏;使用同版本临时容器挂载原数据卷做离线修复

  1. 查看 Swarm service 容器日志,初步判断是数据库内部事务恢复问题,不是 Swarm 网络、端口或资源限制问题;
  2. 进入故障容器查看 mysqld 内核日志,发现关键日志:Found prepared XA transactions,确认存在XA悬停事务

​ 直接进容器看到的异常日志,一直不能明确定位到具体原因。排查服务器磁盘空间都是够用的。

容器内手动前台启动 mysqld(最关键,直接打印崩溃原因)

# 容器内执行
mysqld --datadir=/config/databases

image

  1. 原理回顾:XA 两阶段事务,prepare 成功之后,协调器宕机/网络中断,没有收到 commit/rollback。数据库重启时会尝试自动处理;协调器永久丢失时,自动恢复失败,数据库无法启动;而且容器内的mysql进程无法杀除并重启,因为mysql的容器镜像中加了mysqld-safe的异常重启服务。

  2. 方案:新建临时容器,挂载原有 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 关闭临时库,恢复业务服务

  1. 在 mysqld 运行窗口按 Ctrl+C,优雅关闭数据库进程;
  2. 退出临时容器,容器会自动销毁;
  3. 回到 Docker Swarm,重启 mariadb service:
docker service update --force mariadb-service
  1. 观察容器状态,容器正常启动不再反复crash,业务连接恢复。

四、原理分析

XA分布式事务分为2PC:

  1. Prepare:各资源节点持久化事务,返回成功;
  2. Commit:事务协调器通知所有节点提交。

故障场景:业务XA事务执行到prepare成功,协调器节点崩溃/网络断开,没有下发commit/rollback指令。 数据库重启时,InnoDB引擎会扫描所有prepared状态XA事务,等待协调器指令。协调器永久丢失时,数据库无法自动完成事务处理,数据库启动阻塞。

⚠️ 重要提醒:启发式回滚属于应急修复手段。修复完成后,业务侧需要核对相关业务数据,校验数据一致性。

五、本次故障踩坑清单

  1. 混淆 MySQL8 和 MariaDB 的系统表,MariaDB 10.4 没有 innodb_prepared_transactions,查询会报表不存在;
  2. 临时容器环境缺少 /var/run/mysqld 目录,mysqld 启动时创建pid、socket文件失败,数据库内核已经加载完成但服务退出;
  3. 不要直接在业务Swarm容器内直接修改、启动mysqld,容易引发数据损坏;优先临时容器挂载volume离线修复;
  4. Docker Swarm 服务会自动重启崩溃容器,反复重启会加剧事务恢复异常,故障期间建议先缩容副本为0,避免持续损坏数据。

六、预防方案

  1. XA事务协调器做好状态持久化,记录每个XA事务全局ID和状态,协调器重启后可以继续处理残留事务;
  2. 增加监控:定时执行 XA RECOVER;,一旦检测到pending事务,触发告警,尽早发现;
  3. Swarm中数据库服务配置合理的重启策略,故障时不要无限快速重启,避免放大故障;
  4. 定期备份volume,数据库数据卷损坏时可以快速回滚;
posted @ 2026-09-20 15:15  未来AI笔记  阅读(6)  评论(0)    收藏  举报