1. 为什么我们需要Docker化的MySQL主从复制?
去年我接手一个电商项目,数据库负载在促销期间经常爆表。当时我们尝试用传统方式部署MySQL主从集群,结果光是环境配置就花了三天,还遇到各种版本兼容性问题。直到改用Docker方案后,部署时间缩短到30分钟,这才是现代开发者该有的效率。
Docker化的MySQL主从复制有三大不可替代的优势:
- 环境一致性:再也不用担心"在我机器上是好的"这种问题,镜像即配置
- 快速扩容:一个docker-compose文件就能拉起新的从库节点
- 资源隔离:主从实例各自独立,不会因为从库的复杂查询拖垮主库
重要提示:虽然Docker简化了部署,但MySQL主从复制的核心机制(binlog、GTID等)与传统部署完全一致。理解这些底层原理才能有效排错。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 主从复制核心机制速览
2.1 二进制日志(binlog)的工作逻辑
MySQL的主从复制本质上是binlog的传输与重放。当主库执行写操作时:
- 事务提交时生成binlog事件
- 事件按照
binlog_format(建议用ROW格式)写入文件 - 从库IO线程拉取binlog到本地relay log
- 从库SQL线程重放relay log中的事件
sql复制-- 查看主库binlog状态
SHOW MASTER STATUS;
-- 输出示例
+------------------+----------+--------------+------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+------------------+----------+--------------+------------------+
| mysql-bin.000003 | 785 | | |
+------------------+----------+--------------+------------------+
2.2 GTID复制模式的优势
全局事务标识(Global Transaction Identifier)是MySQL 5.6引入的革命性特性:
- 每个事务有唯一ID格式:
source_id:transaction_id - 从库自动定位断点位置,无需手动指定binlog file/pos
- 支持级联复制和故障切换
ini复制# 必须在my.cnf中显式启用
gtid_mode=ON
enforce_gtid_consistency=ON
3. Docker环境准备与陷阱规避
3.1 网络方案选型:bridge vs host
我强烈建议使用自定义bridge网络而非默认的docker0:
bash复制# 创建专用网络
docker network create mysql-cluster
# 验证网络
docker network inspect mysql-cluster
这样做的原因:
- 容器间可通过容器名直接通信(自动DNS解析)
- 与宿主机网络隔离,避免端口冲突
- 方便后续扩展读写分离中间件
3.2 数据持久化方案对比
常见的数据卷方案:
| 方案类型 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 绑定挂载 | 直观易调试 | 受宿主机权限限制 | 开发环境 |
| 命名卷 | Docker自动管理 | 需要docker volume命令操作 | 生产环境简单部署 |
| 分布式存储插件 | 支持多节点访问 | 配置复杂 | Kubernetes集群环境 |
推荐生产环境使用命名卷:
bash复制docker volume create mysql-master-data
docker volume create mysql-slave-data
4. 主库配置深度解析
4.1 关键配置模板
创建master.cnf配置文件:
ini复制[mysqld]
server-id = 1
log_bin = mysql-bin
binlog_format = ROW
expire_logs_days = 7
sync_binlog = 1
gtid_mode = ON
enforce_gtid_consistency = ON
binlog_group_commit_sync_delay = 100
binlog_group_commit_sync_no_delay_count = 10
# 重要:必须设置唯一UUID
binlog_group_commit_sync_no_delay_count = 10
4.2 启动主库容器
注意这几个关键参数:
bash复制docker run -d \
--name mysql-master \
--network mysql-cluster \
-v mysql-master-data:/var/lib/mysql \
-v $(pwd)/master.cnf:/etc/mysql/conf.d/master.cnf \
-e MYSQL_ROOT_PASSWORD=Master@123 \
-e MYSQL_REPLICATION_USER=repl \
-e MYSQL_REPLICATION_PASSWORD=Repl@123 \
-p 3306:3306 \
mysql:8.0 \
--server-id=1 \
--log-bin=mysql-bin \
--binlog-format=ROW
血泪教训:如果忘记设置server-id,主从复制根本不会工作,但错误日志只会显示模糊的"replication error"!
5. 从库配置与连接主库
5.1 从库配置文件
slave.cnf需要特殊配置:
ini复制[mysqld]
server-id = 2 # 必须大于主库
relay_log = mysql-relay-bin
log_slave_updates = ON
read_only = ON
gtid_mode = ON
enforce_gtid_consistency = ON
skip_slave_start = ON # 防止自动启动复制
5.2 启动从库并建立复制
分步操作指南:
bash复制# 1. 启动从库容器
docker run -d \
--name mysql-slave \
--network mysql-cluster \
-v mysql-slave-data:/var/lib/mysql \
-v $(pwd)/slave.cnf:/etc/mysql/conf.d/slave.cnf \
-e MYSQL_ROOT_PASSWORD=Slave@123 \
-p 3307:3306 \
mysql:8.0
# 2. 进入从库配置复制链路
docker exec -it mysql-slave mysql -uroot -pSlave@123
-- 3. 执行CHANGE MASTER命令
CHANGE MASTER TO
MASTER_HOST='mysql-master',
MASTER_USER='repl',
MASTER_PASSWORD='Repl@123',
MASTER_AUTO_POSITION=1;
-- 4. 启动复制
START SLAVE;
-- 5. 检查状态(关键看Slave_IO_Running和Slave_SQL_Running)
SHOW SLAVE STATUS\G
6. 复制状态监控与排错指南
6.1 关键监控指标
sql复制-- 精简版监控命令
SELECT
REPLICA_HOST_NAME as host,
SERVICE_STATE as io_state,
LAST_ERROR_NUMBER as err_no,
LAST_ERROR_MESSAGE as err_msg
FROM performance_schema.replication_connection_status;
6.2 常见错误解决方案
| 错误现象 | 可能原因 | 解决方案 |
|---|---|---|
| Slave_IO_Running=Connecting | 网络不通/密码错误 | 检查docker网络连通性,验证repl用户权限 |
| Slave_SQL_Running=No | 主从数据不一致 | 在从库执行SET GLOBAL sql_slave_skip_counter=1; START SLAVE; |
| 重复键错误 | 从库已有数据 | 重建从库数据:STOP SLAVE; RESET SLAVE ALL; 然后重新做数据快照 |
| GTID同步失败 | 主库purge了需要的binlog | 在主库执行PURGE BINARY LOGS BEFORE '2023-01-01 00:00:00'; 要谨慎 |
7. 实战验证方案设计
7.1 基础验证流程
bash复制# 在主库创建测试数据
docker exec -it mysql-master mysql -uroot -pMaster@123 -e "CREATE DATABASE repl_test;"
docker exec -it mysql-master mysql -uroot -pMaster@123 -e "CREATE TABLE repl_test.t1(id INT PRIMARY KEY AUTO_INCREMENT, data VARCHAR(100));"
# 在从库检查是否同步
docker exec -it mysql-slave mysql -uroot -pSlave@123 -e "SHOW TABLES FROM repl_test;"
7.2 高级验证:延迟测试
sql复制-- 主库插入带时间戳的数据
INSERT INTO repl_test.t1(data) VALUES (NOW());
-- 从库计算延迟
SELECT
TIMEDIFF(
(SELECT data FROM repl_test.t1 ORDER BY id DESC LIMIT 1),
NOW()
) AS replication_delay;
8. 生产环境优化建议
8.1 参数调优模板
ini复制# 主库追加配置
binlog_group_commit_sync_delay = 100
binlog_group_commit_sync_no_delay_count = 10
binlog_transaction_dependency_tracking = WRITESET
# 从库追加配置
slave_parallel_workers = 4
slave_parallel_type = LOGICAL_CLOCK
8.2 监控集成方案
推荐使用Prometheus+Granafa监控:
- 部署mysqld-exporter
bash复制docker run -d \
--name mysql-master-exporter \
--network mysql-cluster \
-e DATA_SOURCE_NAME="root:Master@123@(mysql-master:3306)/" \
prom/mysqld-exporter
- Grafana导入Dashboard ID 7362
9. 灾备恢复演练
9.1 主库故障转移步骤
sql复制-- 在从库提升为主库
STOP SLAVE;
RESET SLAVE ALL;
SET GLOBAL read_only=OFF;
-- 新主库配置
SET GLOBAL gtid_purged='<原主库的gtid_executed值>';
9.2 数据一致性校验
使用pt-table-checksum工具:
bash复制docker run -it \
--network mysql-cluster \
-v $(pwd):/scripts \
percona/percona-toolkit \
pt-table-checksum h=mysql-master,u=root,p=Master@123 \
--databases=repl_test \
--no-check-binlog-format
10. 那些年我踩过的坑
10.1 Docker时区问题
现象:主从库时间戳数据不一致
解决:所有容器启动时添加-e TZ=Asia/Shanghai
10.2 内存不足导致OOM
现象:从库频繁重启
解决:限制容器内存并调整MySQL缓冲池
bash复制docker update mysql-slave --memory 2G --memory-swap 2G
10.3 大事务阻塞复制
教训:避免在主库执行百万级UPDATE
方案:拆分为小事务,或使用pt-online-schema-change
经过这些优化后,我们的电商系统在618期间实现了数据库请求量提升300%而零宕机的成绩。记住,好的架构不是设计出来的,而是踩坑踩出来的。
