不能直接用deployment部署双主mysql,因其pod名随机、dns不可预测,无法支撑change master to和gtid自动定位;statefulset提供固定主机名(如mysql-0.mysql)、唯一且稳定的server-id映射、独立readwriteonce存储及有序启停,是双主集群可靠运行的基础。

为什么不能直接用 Deployment 部署双主 MySQL
Deployment 生成的 Pod 名字带随机哈希(如 mysql-7d8f9b4c6-xyz12),重启后 DNS 名不可预测,而 MySQL 双主必须靠稳定、可解析的主机名做 CHANGE MASTER TO 和 GTID 自动定位。Headless Service + StatefulSet 才能提供 mysql-0.mysql-hs.mysql-cluster.svc.cluster.local 这类固定 DNS 记录,这是双主通信的基础。用 Deployment 部署双主,大概率出现主从连接失败、GTID 不一致、自动切换卡死等问题。
StatefulSet 中每个 Pod 的 server_id 必须唯一且固定
MySQL 8.0 要求双主间 server_id 全局唯一,且不能随 Pod 重建而变化。StatefulSet 的序号(mysql-0、mysql-1)是稳定不变的,应直接映射为 server_id 值:
- 在
ConfigMap的my.cnf中不要硬编码server_id=1,改用环境变量注入:server_id=${POD_INDEX} - 通过
initContainer或启动脚本从hostname解析出序号(如mysql-1→1),再写入配置或传给 mysqld 启动参数 - 若用 Helm,可用
{{ .Index | add 1 }}动态生成;若纯 YAML,需为每个 Pod 单独定义env,例如mysql-0设POD_INDEX=1,mysql-1设POD_INDEX=2 - 遗漏这点会导致两个实例
server_id冲突,START SLAVE直接报错ERROR 3021 (HY000)
双主集群必须禁用 auto-increment-offset 的默认陷阱
MySQL 8.0 默认 auto_increment_increment=1,双主写入时极易主键冲突。必须显式配置错开的自增步长:
- Pod
mysql-0:设置auto_increment_offset=1,auto_increment_increment=2 - Pod
mysql-1:设置auto_increment_offset=2,auto_increment_increment=2 - 这两项必须写进
my.cnf的[mysqld]段,不能只靠 SQL 设置——因为 Pod 重建后配置会丢失 - 验证方式:进容器执行
mysql -e "SHOW VARIABLES LIKE 'auto_increment%';",确保两节点值严格互补 - 漏配会导致 INSERT 不报错但数据在另一主上因主键冲突被跳过,最终双主数据不一致却无明显告警
PV/PVC 必须用 ReadWriteOnce,别碰 ReadWriteMany
MySQL 是单点写入引擎,双主本质仍是两个独立实例各自写自己的数据目录,绝不能共享同一块存储。NFS 或云盘若设成 ReadWriteMany,两个 mysqld 进程会同时写 ibdata1、redo log,瞬间损坏数据文件。
- 每个 Pod 必须绑定专属 PV,
accessModes: [ReadWriteOnce],且 PV 的nodeAffinity要绑定到对应节点(如mysql-0绑定到 node-A) - StorageClass 的
provisioner推荐用kubernetes.io/no-provisioner(静态 PV),避免动态供给器误分配共享卷 - 如果用本地盘(
hostPath),务必提前在各节点创建隔离目录:/data/mysql-0、/data/mysql-1,并设chmod 700 -R /data/mysql-* - 曾见用户用 NFS + ReadWriteMany 部署双主,跑一天后
mysqld启动失败,日志全是InnoDB: Database page corruption











