事件时间:2026-06-12 08:08-08:09 UTC(你时区 16:06-16:09)
事件摘要: Zeabur K8s 同时启动了新旧两个 MySQL Pod,都挂载同一个 PersistentVolume,导致两个 mysqld 进程抢占同一份 ibdata1 文件。幸运的是 InnoDB 的 file lock 机制阻止了数据损坏,但这依然是一个极高风险的竞态条件 (race condition)。
日志证据:
08:08:10 UTC - 新 Pod 启动(xsjlr)
08:08:11 UTC - 新 mysqld 进程启动
08:08:39 UTC - 新 mysqld 开始尝试锁 ibdata1,失败(error 11 = EAGAIN)
08:08:39-08:09:07 UTC - 连续 28 秒 lock 失败(每秒一次)
08:09:07 UTC - [Zeabur] TaintManagerEviction: Cancelling deletion
08:08:59 UTC - [Zeabur] Killing: Stopping container mysql(旧 Pod)
08:09:05 UTC - 旧 mysqld 收到 SHUTDOWN 信号
08:09:10 UTC - 旧 mysqld 完全关闭
08:09:13 UTC - 新 mysqld InnoDB 初始化完成
08:09:15 UTC - 新 mysqld ready for connections
为什么这很严重:
- InnoDB 的 file lock 是最后一道防线。没有这个锁,两边同时写 ibdata1 会导致数据文件损坏,可能无法恢复。
- 这次运气好:新 mysqld 反复 try lock 失败,完全没写东西;旧 mysqld 优雅关闭,buffer pool flush 完才退出。
- 下次就不一定这么幸运了。
根本原因分析:
- K8s 节点被打了 taint(节点状态异常)。
- K8s 想驱逐 MySQL Pod 到别的节点。
- 先启动新 Pod(在新节点)。
- 然后又“Cancelling deletion”(取消驱逐,可能旧节点恢复了)。
- 但新旧两个 Pod 都挂载了同一个 PersistentVolume。
这是 K8s 调度的竞态条件。MySQL 应该用 StatefulSet + OnDelete 策略,保证旧的彻底死透,新的才启动。
需要 Zeabur 回答的问题:
- 08:08-08:09 UTC 你们 K8s 为什么同时跑了两个 mysqld 抢同一个 PV?
- 为什么出现
TaintManagerEviction: Cancelling?那台节点当时什么状况? - MySQL 是不是用 StatefulSet?调度策略为什么让新旧 Pod 重叠挂载同一个 PV?
- 怎么避免再发生?
影响:
- Django 应用 Connection refused 约 52 秒。
- 用户看到 500 错误。
- 数据有损坏风险(幸运躲过)。