作者:进击的圆儿
日期:2025年10月10日
标签MySQL 主从复制 读写分离 高可用 实战


📑 目录


在这里插入图片描述

🎯 前言:从配置到应用

在上一篇《MySQL主从复制Docker实战(上)》中,我成功配置了1主2从的MySQL集群:

✅ Master:binlog已开启,复制用户已创建
✅ Slave1:IO线程Yes,SQL线程Yes,延迟0秒
✅ Slave2:IO线程Yes,SQL线程Yes,延迟0秒

但是,主从复制配置好了,数据真的能同步吗?读写分离如何实现?

今天的目标:

  • 验证数据实时同步
  • 配置并测试读写分离
  • 分析主从延迟
  • 理解三种复制模式的选择

这篇博客记录了我测试主从复制的全过程,包括思考和收获。


🧪 第一部分:数据同步验证

步骤1:在Master创建测试数据库和表

PS D:\pythontest\c++_learn> docker exec mysql-master mysql -uroot -proot123 -e \
  "CREATE DATABASE test_replication; 
   USE test_replication; 
   CREATE TABLE users (
     id INT PRIMARY KEY, 
     name VARCHAR(50), 
     created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
   );"
mysql: [Warning] Using a password on the command line interface can be insecure.

创建成功!


步骤2:在Master插入测试数据

PS D:\pythontest\c++_learn> docker exec mysql-master mysql -uroot -proot123 -e \
  "USE test_replication; 
   INSERT INTO users (id, name) VALUES 
     (1, 'Alice'), 
     (2, 'Bob'), 
     (3, 'Charlie');"
mysql: [Warning] Using a password on the command line interface can be insecure.

插入成功!

在Master查询验证:

PS D:\pythontest\c++_learn> docker exec mysql-master mysql -uroot -proot123 -e \
  "USE test_replication; SELECT * FROM users;"
mysql: [Warning] Using a password on the command line interface can be insecure.
id      name    created_at
1       Alice   2025-10-10 15:56:52
2       Bob     2025-10-10 15:56:52
3       Charlie 2025-10-10 15:56:52

步骤3:在Slave验证数据同步

Slave1查询:

PS D:\pythontest\c++_learn> docker exec mysql-slave1 mysql -uroot -proot123 -e \
  "USE test_replication; SELECT * FROM users;"
mysql: [Warning] Using a password on the command line interface can be insecure.
id      name    created_at
1       Alice   2025-10-10 15:56:52  ← 和Master完全一致!
2       Bob     2025-10-10 15:56:52
3       Charlie 2025-10-10 15:56:52

Slave2查询:

PS D:\pythontest\c++_learn> docker exec mysql-slave2 mysql -uroot -proot123 -e \
  "USE test_replication; SELECT * FROM users;"
mysql: [Warning] Using a password on the command line interface can be insecure.
id      name    created_at
1       Alice   2025-10-10 15:56:52  ← 和Master完全一致!
2       Bob     2025-10-10 15:56:52
3       Charlie 2025-10-10 15:56:52

在这里插入图片描述

🎉 完美!数据已实时同步到2个Slave!


🔒 第二部分:读写分离实战

配置Slave只读模式

为什么需要只读模式?我们稍后解释,先配置:

Slave1设置为只读:

PS D:\pythontest\c++_learn> docker exec mysql-slave1 mysql -uroot -proot123 -e \
  "SET GLOBAL read_only=1; SET GLOBAL super_read_only=1;"
mysql: [Warning] Using a password on the command line interface can be insecure.

Slave2设置为只读:

PS D:\pythontest\c++_learn> docker exec mysql-slave2 mysql -uroot -proot123 -e \
  "SET GLOBAL read_only=1; SET GLOBAL super_read_only=1;"
mysql: [Warning] Using a password on the command line interface can be insecure.

配置成功!

参数说明:

  • read_only=1:普通用户无法写入
  • super_read_only=1:即使是超级用户(root)也无法写入

测试Slave拒绝写入

尝试在Slave1插入数据:

在这里插入图片描述

🎉 完美!Slave拒绝了写入操作!


为什么需要只读模式?

读写分离架构:

数据库层
应用层
写操作
INSERT/UPDATE/DELETE
读操作
SELECT
读操作
SELECT
binlog复制
binlog复制
Master
可读可写
Slave1
只读
Slave2
只读
Web应用

如果不配置只读模式会怎样?

场景:应用误操作,向Slave写入数据

Slave直接写入:
  INSERT INTO users (id, name) VALUES (4, 'David');
  ↓
Slave本地有id=4的记录
  ↓
Master通过binlog也插入id=4
  ↓
主键冲突!数据不一致!💥

配置只读模式:
  INSERT操作被拒绝 ✅
  强制所有写操作走Master
  保证数据一致性

⏱️ 第三部分:主从延迟测试

批量插入数据测试

PS D:\pythontest\c++_learn> docker exec mysql-master mysql -uroot -proot123 -e \
  "USE test_replication; 
   INSERT INTO users (id, name) VALUES 
     (4, 'David'), (5, 'Eve'), (6, 'Frank'), 
     (7, 'Grace'), (8, 'Henry'), (9, 'Ivy'), (10, 'Jack');"
mysql: [Warning] Using a password on the command line interface can be insecure.

插入7条数据成功!


查看Slave延迟

在这里插入图片描述

验证Slave1数据:

PS D:\pythontest\c++_learn> docker exec mysql-slave1 mysql -uroot -proot123 -e \
  "USE test_replication; 
   SELECT COUNT(*) as total, MIN(id) as first_id, MAX(id) as last_id FROM users;"
mysql: [Warning] Using a password on the command line interface can be insecure.
total   first_id  last_id
10      1         1010条数据全部同步!

为什么延迟是0秒?

Seconds_Behind_Master的含义:

Seconds_Behind_Master = Master当前时间 - Slave正在执行的binlog事件的时间戳

延迟0秒的2种情况:
1. 真的没有延迟(IO和SQL都跟上了)
2. Slave已经追上Master(没有新的binlog可读)

我们的情况:
  Read_Master_Log_Pos = Exec_Master_Log_Pos = 2245
  ↓
  已读取 = 已执行 = 完全同步 ✅

主从延迟的影响因素:

  • 网络带宽
  • Slave硬件性能
  • binlog大小
  • SQL复杂度

🔀 第四部分:三种复制模式选择

异步复制 vs 半同步 vs 全同步

在学习过程中,我遇到了一个经典场景问题:

模式 Master什么时候返回成功 优点 缺点
异步复制 写完自己的binlog就返回 主库宕机可能丢数据
半同步复制 至少1个Slave确认收到binlog 平衡 稍慢
全同步复制 所有Slave都执行完才返回 数据最安全 很慢
应用 Master Slave1 Slave2 异步复制(默认) INSERT操作 写binlog 返回成功 ✅ 发送binlog(异步) 发送binlog(异步) 半同步复制 INSERT操作 写binlog 发送binlog 确认收到 返回成功 ✅ 发送binlog(异步) 全同步复制 INSERT操作 写binlog 发送binlog 发送binlog 执行SQL 执行SQL 确认完成 确认完成 返回成功 ✅ 应用 Master Slave1 Slave2

不同场景如何选择?

我被问到了3个场景选择题:

Q1:如果你的项目是【微信聊天】,用户发消息必须立即看到"发送成功",你选哪个模式?

我的回答: 异步复制

理由:

  • 速度第一,用户体验优先
  • 消息丢失可以从客户端重发
  • QPS很高,不能让等待影响性能

Q2:如果你的项目是【支付系统】,扣款记录绝对不能丢,你选哪个模式?

我最初的回答: 全同步复制

但经过思考后修正为: 半同步复制 ✅

理由:

全同步的问题:

用户点击"确认支付" 
  ↓
等Master写完binlog
  ↓
等所有Slave都执行完SQL
  ↓  (假设有5个Slave,最慢的那个花了3秒)
等所有Slave都确认
  ↓
页面才显示"支付成功"

问题:
- 如果有1个Slave网络慢,用户要等很久
- 用户以为卡死了,多次点击支付按钮
- 用户体验极差

半同步的优势:

只需要至少1个Slave确认收到binlog
  ↓
数据已经持久化到2个节点(Master + Slave1)
  ↓
即使Master宕机,Slave1也有完整数据
  ↓
数据安全 + 性能可接受 ✅

Q3:如果你的项目是【论坛评论】,偶尔丢一两条评论也能接受,你选哪个模式?

我最初的回答: 半同步复制

但经过思考后修正为: 异步复制 ✅

理由:

既然"能接受丢失",为什么还要用半同步(需要等从库确认)?

异步复制最坏情况:
  丢失Master宕机前2-3秒内的评论
  
论坛场景下:
  用户刷新页面重新发一次就行
  不影响核心业务
  优先保证性能 ✅

我的理解总结

场景 最佳模式 原因
微信聊天 异步复制 速度第一,消息可重发
支付系统 半同步复制 至少1个从库有binlog,主库宕机不丢钱 ✅
论坛评论 异步复制 丢失几秒评论可接受,速度优先
电商订单 半同步复制 订单数据不能丢,但不能太慢
日志系统 异步复制 丢失少量日志可接受,吞吐量优先

📝 总结

今天的收获

通过这次读写分离和高可用测试,我掌握了:

数据同步验证

  • Master写入 → Slave自动同步
  • Seconds_Behind_Master = 0(实时同步)

读写分离实战

  • Slave只读模式配置(read_only + super_read_only)
  • 强制所有写操作走Master,保证数据一致性

主从延迟理解

  • Seconds_Behind_Master的含义
  • Read_Master_Log_Pos vs Exec_Master_Log_Pos

复制模式选择

  • 异步:快,可能丢数据
  • 半同步:平衡,至少1个Slave确认
  • 全同步:最安全,但很慢
  • 根据业务场景选择,不是越安全越好!

生产环境最佳实践

  1. 监控告警

    • 监控Seconds_Behind_Master(延迟超过5秒告警)
    • 监控Slave_IO_Running和Slave_SQL_Running(不是Yes立即告警)
    • 工具:Prometheus + Grafana + Alertmanager
  2. 高可用方案

    • 多Slave(至少2个,最好3个)
    • 自动故障切换(MHA、Orchestrator)
    • VIP漂移(Keepalived)
  3. 读写分离方案

    • 应用层:代码判断(写走Master,读走Slave)
    • 中间件:ProxySQL、MySQL Router
    • 框架:ShardingSphere
  4. 数据备份

    • 从Slave备份(不影响Master性能)
    • 定期全量备份 + binlog增量备份
    • 异地容灾

系列回顾

通过这3篇实战博客,我完整掌握了MySQL主从复制:

博客3:Docker入门

  • Docker vs 虚拟机
  • 容器、镜像、网络
  • 搭建1主2从容器环境

博客4:主从复制配置

  • Master配置:binlog、复制用户
  • Slave配置:CHANGE MASTER、START SLAVE
  • 3个真实问题排查

博客5:读写分离测试

  • 数据同步验证
  • 只读模式配置
  • 三种复制模式选择

参考资料


如果这篇文章对你有帮助,欢迎点赞收藏!
有问题欢迎在评论区讨论~


系列文章:

Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐