MySQL主从复制与高可用搭建教程:从原理到实战的完整指南
作者:阿狗童鞋
还在为MySQL单点故障和数据丢失发愁?本文从主从复制原理讲起,手把手教你搭建读写分离和半同步复制,对比MHA、Orchestrator等高可用方案,并附常见坑速查表,掌握这些,让你的数据库挂了一台系统还在跑,数据不丢、业务不中断
单库单机是"能用",主从复制是"可靠",高可用是"挂了一台系统还在跑"。MySQL 主从复制与高可用是 DBA 和后端开发的必备技能,也是 CSDN 数据库板块最热门的话题。
本文从为什么要主从讲起,覆盖复制原理、搭建配置、读写分离、半同步复制、高可用方案(MHA/Orchestrator/Keepalived)、常见坑。
一、为什么需要主从复制
1.1 单库的问题
单库单机: - 读多写少的业务 → 一台库扛不住读压力 - 主库挂了 → 整个系统不可用 - 备份/恢复期间 → 业务停顿
1.2 主从复制的价值
| 价值 | 说明 |
|---|---|
| 读写分离 | 主库写、从库读,扩展读能力 |
| 高可用 | 主库挂 → 从库提升为主库 |
| 数据备份 | 从库可作为备份源 |
| 灾备 | 跨机房/跨地域从库 |
二、复制原理(必考)
2.1 复制流程
主库:写操作 → Binlog(二进制日志)
从库:IO 线程拉取 Binlog → 写入中继日志(Relay Log)
SQL 线程回放 Relay Log → 应用到从库数据
主库 从库 写入 → Binlog ──(IO线程拉取)──→ Relay Log ──(SQL线程回放)──→ 应用
2.2 三种复制方式
| 方式 | 同步性 | 一致性 |
|---|---|---|
| 异步复制 | 主库不等待从库 | 可能丢失(主挂时) |
| 半同步复制 | 至少一个从库确认 | 主挂不丢已确认事务 |
| 组复制(Group Replication) | 多数派确认 | 强一致(MySQL 8) |
2.3 Binlog 格式
STATEMENT:记录 SQL(占用小,主从数据可能不一致) ROW:记录行变更(占用大,最安全,默认推荐) MIXED:混合
推荐 ROW——数据一致且能精确恢复。
三、搭建主从复制(实战)
3.1 环境准备
主库:192.168.1.10 从库:192.168.1.11 MySQL 版本:8.0(两边版本一致)
3.2 主库配置
# my.cnf(主库) [mysqld] server-id = 1 # 唯一 log-bin = mysql-bin # 开启 Binlog binlog-format = ROW # ROW 格式 binlog-expire-logs-seconds = 2592000 # 保留 30 天
# 重启主库 systemctl restart mysqld
3.3 主库创建复制账号
-- 登录主库创建复制用户 CREATE USER 'repl'@'192.168.1.%' IDENTIFIED BY 'Repl@123456'; GRANT REPLICATION SLAVE ON *.* TO 'repl'@'192.168.1.%'; FLUSH PRIVILEGES; -- 查看当前 Binlog 位置(从库要用) SHOW MASTER STATUS; -- +------------------+----------+------------------+ -- | File | Position | Binlog_Do_DB | -- +------------------+----------+------------------+ -- | mysql-bin.000001 | 154 | | -- +------------------+----------+------------------+
3.4 从库配置
# my.cnf(从库) [mysqld] server-id = 2 # 不能和主库一样 read_only = 1 # 从库只读(防误写)
systemctl restart mysqld
3.5 从库启动复制
-- 登录从库 CHANGE MASTER TO MASTER_HOST='192.168.1.10', MASTER_USER='repl', MASTER_PASSWORD='Repl@123456', MASTER_LOG_FILE='mysql-bin.000001', MASTER_LOG_POS=154; START SLAVE; -- 查看复制状态(重点!) SHOW SLAVE STATUS\G; -- 关键字段: -- Slave_IO_Running: Yes ← IO 线程正常 -- Slave_SQL_Running: Yes ← SQL 线程正常 -- Seconds_Behind_Master: 0 ← 延迟 0 秒 -- Last_Error: ← 最近错误(为空)
3.6 验证
-- 主库建表插数 CREATE DATABASE test; USE test; CREATE TABLE t (id INT PRIMARY KEY, name VARCHAR(20)); INSERT INTO t VALUES (1, '张三'); -- 从库查询(数据已同步) USE test; SELECT * FROM t; -- 能查到 → 复制成功
验证三要素:Slave_IO_Running=Yes、Slave_SQL_Running=Yes、Seconds_Behind_Master=0。
四、读写分离(应用层)
4.1 方案选择
| 方案 | 方式 | 特点 |
|---|---|---|
| 应用层(推荐) | 框架多数据源 | 灵活可控 |
| 中间件(ProxySQL/MyCat) | 独立代理 | 对应用透明 |
| 客户端插件(ShardingSphere) | 数据源路由 | 集成度高 |
4.2 Spring Boot + ShardingSphere 读写分离
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
<version>5.4.0</version>
</dependency>spring:
shardingsphere:
datasource:
names: master,slave0
master:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://192.168.1.10:3306/mall
username: root
password: 123456
slave0:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://192.168.1.11:3306/mall
username: root
password: 123456
rules:
readwrite-splitting:
data-sources:
ds:
write-data-source-name: master
read-data-source-names:
- slave0
props:
sql-show: true// 业务代码不用改!注解控制路由
@Transactional // 事务内走主库(防止读不到刚写的数据)
public void createOrder(Order order) {
orderMapper.insert(order);
}
public Order getOrder(Long id) {
return orderMapper.selectById(id); // 默认走从库
}
关键:强一致读必须走主库(或主从延迟容忍度内再走从库)——用 @Transactional 或强制路由注解。
五、主从延迟(最核心的坑)
5.1 为什么有延迟
从库 SQL 线程回放是单线程的(MySQL 8 之前) 主库并发写入 → 从库串行回放 → 追不上 → 延迟累积
5.2 监控延迟
SHOW SLAVE STATUS\G; -- Seconds_Behind_Master: 5 ← 延迟秒数 -- 持续增长说明从库跟不上
5.3 解决延迟
| 手段 | 说明 |
|---|---|
| 并行复制 | slave-parallel-workers=8(MySQL 8 默认开启) |
| 减少大事务 | 一次别更新太多行 |
| 索引优化 | 回放慢多因更新语句全表扫 |
| 读写分离兜底 | 延迟敏感场景强制走主库 |
| 分库分表 | 单库压力下降延迟自然降 |
# 从库并行复制配置 [mysqld] slave-parallel-type = LOGICAL_CLOCK slave-parallel-workers = 8
六、半同步复制(防丢数据)
6.1 异步的问题
异步复制:主库写完 Binlog 就返回成功 → 主库突然宕机 → 新主库没有最后几条事务 → 数据丢失
6.2 半同步原理
主库事务提交 → 等待至少一个从库确认收到 → 才返回成功
实现:MySQL 半同步插件(semisync)。
# 主库加载插件 INSTALL PLUGIN rpl_semi_sync_source SONAME 'semisync_source.so'; SET GLOBAL rpl_semi_sync_source_enabled = 1; # 从库 INSTALL PLUGIN rpl_semi_sync_replica SONAME 'semisync_replica.so'; SET GLOBAL rpl_semi_sync_replica_enabled = 1; # 重启复制 STOP SLAVE; START SLAVE;
效果:主库挂了,已确认事务不丢(在从库有副本);代价是写入性能下降(等一个从库确认)。
MySQL 8.0.26+:原生支持 rpl_semi_sync_source_enabled,配置更简单。
七、高可用方案
7.1 目标
主库宕机 → 自动选新主 → 应用无感知(秒级切换)→ 数据不丢
7.2 主流方案对比
| 方案 | 原理 | 切换速度 | 特点 |
|---|---|---|---|
| MHA | 脚本管理主从,自动提升 | 10-30 秒 | 经典、稳定 |
| Orchestrator | 拓扑管理 + 自动故障转移 | 秒级 | 现代、推荐 |
| Keepalived + 主从 | VIP 漂移 | 秒级 | 简单但可能脑裂 |
| MGR(组复制) | 多数派协议 | 自动 | MySQL 8 原生 |
7.3 Orchestrator(现代推荐)
# 安装 orchestrator(管理所有 MySQL 实例) docker run -d \ --name orchestrator \ -p 3000:3000 \ -v /etc/orchestrator.conf.json:/etc/orchestrator.conf.json \ orchestrator/orchestrator # 界面:http://localhost:3000 # 功能:拓扑可视化、一键切换、自动故障转移
// orchestrator.conf.json 关键配置
{
"MySQLTopologyUser": "orc",
"MySQLTopologyPassword": "orc123456",
"DetectClusterAliasQuery": "SELECT @@hostname",
"RecoverMasterCandidateServers": ["192.168.1.11"],
"PreventCrossDataCenterMasterFailover": false
}切换流程(Orchestrator 自动):
主库宕机 → 检测失败 → 选出候选从库 → 补拉缺失 Binlog → 提升为新主 → 其他从库重指新主
7.4 Keepalived + VIP(简单版)
# 主备各装 keepalived,虚拟 IP 在主备间漂移 # vrrp_instance 配置: # 主库:state MASTER # 从库:state BACKUP # 应用只连虚拟 IP,主挂 VIP 自动漂到从库
# 主库检查脚本(MySQL 挂了就降级) killall -0 mysqld || exit 1
注意:Keepalived 方案切换快但可能脑裂(两个都认为自己是主)——必须配 MySQL 层面的保护和数据一致性兜底。
八、常见坑速查
| 现象 | 原因 | 解决 |
|---|---|---|
| Slave_IO_Running: No | 网络/账号/位置错 | 检查主从连通、账号权限、Binlog 位置 |
| Slave_SQL_Running: No | 回放出错(主从数据冲突) | 看 Last_Error,跳过/重建从库 |
| 从库数据不一致 | STATEMENT 格式 | 换 ROW 格式重建从库 |
| 延迟持续增长 | 大事务/单线程回放 | 并行复制 + 拆大事务 |
| 主挂后丢数据 | 异步复制 | 半同步 + 高可用方案 |
| 从库被写坏 | 没开只读 | read_only = 1 |
| 切换后数据不一致 | 没补 Binlog | Orchestrator 自动补/手动补 |
| 两边版本不一致 | 协议不兼容 | 升级从库到同版本 |
总结
MySQL 高可用的落地主线:主从复制(Binlog → Relay Log → 回放)→ 读写分离(应用层/中间件)→ 半同步(防丢)→ 自动故障转移(Orchestrator/MHA)。
实践要点:ROW 格式、从库只读、监控延迟三要素、延迟敏感读走主库。
记住一句话:复制保数据可用,切换保系统可用,两者配合才是高可用。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。
