从库启动后,指向主库并开始复制。使用 GTID 时无需关心具体位点:
-- 从库执行
CHANGE MASTER TO
MASTER_HOST='10.0.0.10',
MASTER_USER='repl',
MASTER_PASSWORD='StrongReplPass!2026',
MASTER_AUTO_POSITION=1; -- 基于 GTID 自动定位
START SLAVE;
3.3 验证复制状态
-- 从库执行
SHOW SLAVE STATUS\G
-- 关键字段需为 Yes:
-- Slave_IO_Running: Yes
-- Slave_SQL_Running: Yes
-- Seconds_Behind_Master: 0 (延迟秒数,理想为 0)
-- 主库写入一条测试数据
INSERT INTO test.repl_check(ts) VALUES(NOW());
-- 从库查询应能看到
SELECT * FROM test.repl_check;
四、读写分离:在应用层落地
复制搭好后,下一步是让读请求走从库、写请求走主库。常见落地方式有两种。
4.1 中间件方案
ProxySQL / ShardingSphere-Proxy 在数据库前面做 SQL 解析与路由,对应用透明,支持权重、健康检查、故障自动摘除。适合多语言、多服务共用一套数据库的场景,运维成本略高。
4.2 代码层路由(Spring 多数据源)
轻量做法是应用内配置主从两个数据源,根据方法注解或事务类型路由。下面是基于 Spring 的简化示例:
// 用注解标记读操作,AOP 切到从库数据源
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ReadOnly {}
// 事务切面:有写事务走主库,@ReadOnly 走从库
@Around("@annotation(ReadOnly) || @annotation(Transactional)")
public Object route(ProceedingJoinPoint pjp) throws Throwable {
boolean readOnly = methodHasAnnotation(pjp, ReadOnly.class)
|| isReadOnlyTransaction(pjp);
DataSourceContext.set(readOnly ? SLAVE : MASTER);
try { return pjp.proceed(); }
finally { DataSourceContext.clear(); }
}
// 使用
@ReadOnly
public ProductDetail getProduct(Long id) {
return productMapper.selectById(id); // 自动路由到从库
}
注意:在一个写事务内紧接着读刚写入的数据,必须从主库读,否则会因复制延迟读到旧值。简单稳妥的策略是”事务内一律走主库“。
五、生产避坑:延迟、脑裂与监控
主从架构上线后,真正的挑战在运维。下表汇总最常见的问题与对策:
| 现象 | 根因 | 对策 |
|---|---|---|
| Seconds_Behind_Master 持续升高 | 从库慢 SQL、单线程回放瓶颈、大事务 | 优化从库查询;8.0 开启多线程复制(MTS);拆分大事务 |
| 主从数据不一致 | 从库被误写、复制报错跳过 | 从库设 super_read_only;用 pt-table-checksum 定期校验 |
| 主库宕机后脑裂 | 多从提升、VIP 漂移冲突 | 用 Orchestrator/MHA 做自动故障切换,加防双写 fencing |
| 复制中断(SQL 线程报错) | 从库缺失对象、数据冲突 | 定位 GTID 缺口;必要时跳过或重做从库 |
性能问题排查时,MySQL EXPLAIN 实战:读懂执行计划优化慢查询 能帮你定位从库上的慢回放语句;而复制链路上的死锁则可在 MySQL 死锁排查与预防实战 中找到系统化解法。
六、与连接池、备份的协同
读写分离后,主从各自都需要独立的连接池配置。从库通常承载更多读连接,可把最大连接数调高;主库则侧重控制写并发。连接池参数(如 HikariCP 的 maximumPoolSize、connectionTimeout)若设置不当,会引发连接耗尽雪崩,相关内容见 PgBouncer 实战:PostgreSQL 连接池调优,思路与 MySQL 通用。
最后提醒:主从复制不能替代备份。从库会同步你的误删操作,因此必须保留独立的时间点备份(全量 + binlog)。把备份放在从库执行,既不影响主库性能,又能随时基于 binlog 做定点恢复。
七、小结
主从复制与读写分离是 MySQL 扩展读能力、提升可用性的标准解法:以 binlog 为桥梁,用 GTID 简化位点管理,在中间件或代码层完成读写路由,再用监控与校验兜住延迟与一致性风险。搭好这套底座,你的数据库就能从容应对读流量的增长与突发故障。
重启主库后,创建一个专用于复制的账号,并授予 REPLICATION 权限:
-- 主库执行
CREATE USER 'repl'@'10.0.0.%' IDENTIFIED BY 'StrongReplPass!2026';
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'10.0.0.%';
FLUSH PRIVILEGES;
-- 记录当前 binlog 位点(使用 GTID 可跳过此步)
SHOW MASTER STATUS;
-- 输出示例: File=mysql-bin.000003 Position=157
3.2 从库配置与挂载
# 从库 my.cnf
server-id = 2 # 必须唯一,不能和主库或其他从库重复
relay_log = mysql-relay-bin
read_only = ON # 从库只读,防止误写
super_read_only = ON # 连超级用户也禁止写(8.0+)
log_slave_updates = ON # 级联复制时需要
从库启动后,指向主库并开始复制。使用 GTID 时无需关心具体位点:
-- 从库执行
CHANGE MASTER TO
MASTER_HOST='10.0.0.10',
MASTER_USER='repl',
MASTER_PASSWORD='StrongReplPass!2026',
MASTER_AUTO_POSITION=1; -- 基于 GTID 自动定位
START SLAVE;
3.3 验证复制状态
-- 从库执行
SHOW SLAVE STATUS\G
-- 关键字段需为 Yes:
-- Slave_IO_Running: Yes
-- Slave_SQL_Running: Yes
-- Seconds_Behind_Master: 0 (延迟秒数,理想为 0)
-- 主库写入一条测试数据
INSERT INTO test.repl_check(ts) VALUES(NOW());
-- 从库查询应能看到
SELECT * FROM test.repl_check;
四、读写分离:在应用层落地
复制搭好后,下一步是让读请求走从库、写请求走主库。常见落地方式有两种。
4.1 中间件方案
ProxySQL / ShardingSphere-Proxy 在数据库前面做 SQL 解析与路由,对应用透明,支持权重、健康检查、故障自动摘除。适合多语言、多服务共用一套数据库的场景,运维成本略高。
4.2 代码层路由(Spring 多数据源)
轻量做法是应用内配置主从两个数据源,根据方法注解或事务类型路由。下面是基于 Spring 的简化示例:
// 用注解标记读操作,AOP 切到从库数据源
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ReadOnly {}
// 事务切面:有写事务走主库,@ReadOnly 走从库
@Around("@annotation(ReadOnly) || @annotation(Transactional)")
public Object route(ProceedingJoinPoint pjp) throws Throwable {
boolean readOnly = methodHasAnnotation(pjp, ReadOnly.class)
|| isReadOnlyTransaction(pjp);
DataSourceContext.set(readOnly ? SLAVE : MASTER);
try { return pjp.proceed(); }
finally { DataSourceContext.clear(); }
}
// 使用
@ReadOnly
public ProductDetail getProduct(Long id) {
return productMapper.selectById(id); // 自动路由到从库
}
注意:在一个写事务内紧接着读刚写入的数据,必须从主库读,否则会因复制延迟读到旧值。简单稳妥的策略是”事务内一律走主库“。
五、生产避坑:延迟、脑裂与监控
主从架构上线后,真正的挑战在运维。下表汇总最常见的问题与对策:
| 现象 | 根因 | 对策 |
|---|---|---|
| Seconds_Behind_Master 持续升高 | 从库慢 SQL、单线程回放瓶颈、大事务 | 优化从库查询;8.0 开启多线程复制(MTS);拆分大事务 |
| 主从数据不一致 | 从库被误写、复制报错跳过 | 从库设 super_read_only;用 pt-table-checksum 定期校验 |
| 主库宕机后脑裂 | 多从提升、VIP 漂移冲突 | 用 Orchestrator/MHA 做自动故障切换,加防双写 fencing |
| 复制中断(SQL 线程报错) | 从库缺失对象、数据冲突 | 定位 GTID 缺口;必要时跳过或重做从库 |
性能问题排查时,MySQL EXPLAIN 实战:读懂执行计划优化慢查询 能帮你定位从库上的慢回放语句;而复制链路上的死锁则可在 MySQL 死锁排查与预防实战 中找到系统化解法。
六、与连接池、备份的协同
读写分离后,主从各自都需要独立的连接池配置。从库通常承载更多读连接,可把最大连接数调高;主库则侧重控制写并发。连接池参数(如 HikariCP 的 maximumPoolSize、connectionTimeout)若设置不当,会引发连接耗尽雪崩,相关内容见 PgBouncer 实战:PostgreSQL 连接池调优,思路与 MySQL 通用。
最后提醒:主从复制不能替代备份。从库会同步你的误删操作,因此必须保留独立的时间点备份(全量 + binlog)。把备份放在从库执行,既不影响主库性能,又能随时基于 binlog 做定点恢复。
七、小结
主从复制与读写分离是 MySQL 扩展读能力、提升可用性的标准解法:以 binlog 为桥梁,用 GTID 简化位点管理,在中间件或代码层完成读写路由,再用监控与校验兜住延迟与一致性风险。搭好这套底座,你的数据库就能从容应对读流量的增长与突发故障。
当单机 MySQL 扛不住读流量、或一次误删需要快速恢复时,MySQL 主从复制与读写分离几乎是每个后端系统的必经之路。本文用可落地的配置与代码,带你从零搭建一主一从复制,并在应用层实现读写分离,避开生产环境最常见的复制延迟与数据不一致坑。
一、为什么需要主从复制与读写分离
绝大多数业务都是”读多写少”:电商商品详情、内容 feed、报表查询,读请求往往占 80% 以上。如果所有请求都打在同一台主库上,CPU 和连接数很快成为瓶颈。主从复制(Replication)把主库的变更同步到一个或多个从库,再配合读写分离把读流量导到从库,就能线性扩展读能力,同时为主库腾出写资源。
除此之外,主从架构还有两个隐性价值:一是异地容灾,从库可以部署在不同可用区,主库宕机时快速提升从库为主;二是在线备份,备份操作放到从库执行,避免锁表影响线上。关于备份的更多细节,可以参考 MySQL 备份恢复实战:全量备份与 binlog 恢复。
二、主从复制原理:binlog 与三种模式
MySQL 主从复制的基石是 binlog(二进制日志)。主库把所有数据变更(DDL、DML)按顺序写入 binlog,从库的 I/O 线程拉取这些日志,写入本地的 relay log(中继日志),再由 SQL 线程回放,从而实现数据一致。
2.1 异步复制(默认)
主库提交事务后立即返回客户端,不等待从库确认。性能最好,但存在数据丢失窗口:主库宕机时尚未同步的少量事务会丢失。这是默认模式,适合对一致性要求不极端、追求吞吐的场景。
2.2 半同步复制
主库提交后,至少等待一个从库接收并写入 relay log 才返回。牺牲少量延迟,换来”已提交事务至少落在一台从库”的保证,是金融、订单类系统的推荐配置。开启方式是在主从两端安装 rpl_semi_sync_master / rpl_semi_sync_slave 插件。
2.3 延迟问题与一致性
无论哪种模式,从库都存在一定复制延迟(seconds_behind_master)。大事务、从库慢查询、网络抖动都会放大延迟。理解事务隔离与可见性边界,对设计读写分离路由至关重要,建议先读 事务隔离级别实战:脏读、不可重复读、幻读与 MVCC。
三、一步一步搭建主从复制
下面以一主一从(MySQL 8.0)为例,给出最小可用配置。假设主库 IP 为 10.0.0.10,从库为 10.0.0.11。
3.1 主库配置 my.cnf
# 主库 my.cnf
server-id = 1
log_bin = mysql-bin
binlog_format = ROW # 行格式,最安全,便于从库回放
binlog_row_image = FULL
expire_logs_days = 7 # 自动清理旧 binlog,避免磁盘打满
gtid_mode = ON # 建议开启 GTID,故障切换更省心
enforce_gtid_consistency = ON
重启主库后,创建一个专用于复制的账号,并授予 REPLICATION 权限:
-- 主库执行
CREATE USER 'repl'@'10.0.0.%' IDENTIFIED BY 'StrongReplPass!2026';
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'10.0.0.%';
FLUSH PRIVILEGES;
-- 记录当前 binlog 位点(使用 GTID 可跳过此步)
SHOW MASTER STATUS;
-- 输出示例: File=mysql-bin.000003 Position=157
3.2 从库配置与挂载
# 从库 my.cnf
server-id = 2 # 必须唯一,不能和主库或其他从库重复
relay_log = mysql-relay-bin
read_only = ON # 从库只读,防止误写
super_read_only = ON # 连超级用户也禁止写(8.0+)
log_slave_updates = ON # 级联复制时需要
从库启动后,指向主库并开始复制。使用 GTID 时无需关心具体位点:
-- 从库执行
CHANGE MASTER TO
MASTER_HOST='10.0.0.10',
MASTER_USER='repl',
MASTER_PASSWORD='StrongReplPass!2026',
MASTER_AUTO_POSITION=1; -- 基于 GTID 自动定位
START SLAVE;
3.3 验证复制状态
-- 从库执行
SHOW SLAVE STATUS\G
-- 关键字段需为 Yes:
-- Slave_IO_Running: Yes
-- Slave_SQL_Running: Yes
-- Seconds_Behind_Master: 0 (延迟秒数,理想为 0)
-- 主库写入一条测试数据
INSERT INTO test.repl_check(ts) VALUES(NOW());
-- 从库查询应能看到
SELECT * FROM test.repl_check;
四、读写分离:在应用层落地
复制搭好后,下一步是让读请求走从库、写请求走主库。常见落地方式有两种。
4.1 中间件方案
ProxySQL / ShardingSphere-Proxy 在数据库前面做 SQL 解析与路由,对应用透明,支持权重、健康检查、故障自动摘除。适合多语言、多服务共用一套数据库的场景,运维成本略高。
4.2 代码层路由(Spring 多数据源)
轻量做法是应用内配置主从两个数据源,根据方法注解或事务类型路由。下面是基于 Spring 的简化示例:
// 用注解标记读操作,AOP 切到从库数据源
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ReadOnly {}
// 事务切面:有写事务走主库,@ReadOnly 走从库
@Around("@annotation(ReadOnly) || @annotation(Transactional)")
public Object route(ProceedingJoinPoint pjp) throws Throwable {
boolean readOnly = methodHasAnnotation(pjp, ReadOnly.class)
|| isReadOnlyTransaction(pjp);
DataSourceContext.set(readOnly ? SLAVE : MASTER);
try { return pjp.proceed(); }
finally { DataSourceContext.clear(); }
}
// 使用
@ReadOnly
public ProductDetail getProduct(Long id) {
return productMapper.selectById(id); // 自动路由到从库
}
注意:在一个写事务内紧接着读刚写入的数据,必须从主库读,否则会因复制延迟读到旧值。简单稳妥的策略是”事务内一律走主库“。
五、生产避坑:延迟、脑裂与监控
主从架构上线后,真正的挑战在运维。下表汇总最常见的问题与对策:
| 现象 | 根因 | 对策 |
|---|---|---|
| Seconds_Behind_Master 持续升高 | 从库慢 SQL、单线程回放瓶颈、大事务 | 优化从库查询;8.0 开启多线程复制(MTS);拆分大事务 |
| 主从数据不一致 | 从库被误写、复制报错跳过 | 从库设 super_read_only;用 pt-table-checksum 定期校验 |
| 主库宕机后脑裂 | 多从提升、VIP 漂移冲突 | 用 Orchestrator/MHA 做自动故障切换,加防双写 fencing |
| 复制中断(SQL 线程报错) | 从库缺失对象、数据冲突 | 定位 GTID 缺口;必要时跳过或重做从库 |
性能问题排查时,MySQL EXPLAIN 实战:读懂执行计划优化慢查询 能帮你定位从库上的慢回放语句;而复制链路上的死锁则可在 MySQL 死锁排查与预防实战 中找到系统化解法。
六、与连接池、备份的协同
读写分离后,主从各自都需要独立的连接池配置。从库通常承载更多读连接,可把最大连接数调高;主库则侧重控制写并发。连接池参数(如 HikariCP 的 maximumPoolSize、connectionTimeout)若设置不当,会引发连接耗尽雪崩,相关内容见 PgBouncer 实战:PostgreSQL 连接池调优,思路与 MySQL 通用。
最后提醒:主从复制不能替代备份。从库会同步你的误删操作,因此必须保留独立的时间点备份(全量 + binlog)。把备份放在从库执行,既不影响主库性能,又能随时基于 binlog 做定点恢复。
七、小结
主从复制与读写分离是 MySQL 扩展读能力、提升可用性的标准解法:以 binlog 为桥梁,用 GTID 简化位点管理,在中间件或代码层完成读写路由,再用监控与校验兜住延迟与一致性风险。搭好这套底座,你的数据库就能从容应对读流量的增长与突发故障。




