深入理解MySQL主从复制原理、配置步骤以及读写分离的实现方案,附完整配置示例
一、背景
在面对高并发读写场景时,单一数据库实例往往成为系统瓶颈。主从复制 + 读写分离是解决这一问题的经典方案,也是后端开发者进阶必须掌握的核心技能。
二、主从复制原理
2.1 核心概念
MySQL主从复制基于binlog日志实现,主要包含三个线程:
| 线程 | 作用 | 所在节点 |
|---|---|---|
| Binlog Dump | 读取binlog并发送给从库 | 主库 |
| IO Thread | 接收主库发送的binlog | 从库 |
| SQL Thread | 重放relay log中的SQL | 从库 |
2.2 复制流程
主库: INSERT → binlog → Binlog Dump →──────→ IO Thread → relay log → SQL Thread → 从库
- 主库执行SQL,生成binlog
- Binlog Dump线程读取binlog,发送给从库
- 从库IO线程接收,写入relay log
- 从库SQL线程重放relay log中的SQL
2.3 复制模式
| 模式 | 特点 | 异步/同步 |
|---|---|---|
| 异步复制 | 主库不等待从库确认 | 异步 |
| 半同步复制 | 主库等待至少一个从库确认 | 半同步 |
| 全同步复制 | 主库等待所有从库确认 | 同步 |
三、实战配置
3.1 环境说明
192.168.1.10 - 主库 (Master)
192.168.1.20 - 从库 (Slave)
3.2 主库配置
bash
# /etc/mysql/my.cnf
[mysqld]
server-id = 1
log_bin = /var/log/mysql/mysql-bin
binlog_format = ROW
sync_binlog = 1
3.3 创建复制账号
sql
-- 主库执行
CREATE USER 'repl'@'%' IDENTIFIED BY 'repl_password';
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%';
FLUSH PRIVILEGES;
3.4 从库配置
bash
# /etc/mysql/my.cnf
[mysqld]
server-id = 2
relay_log = /var/log/mysql/mysql-relay-bin
read_only = 1
3.5 启动复制
sql
-- 从库执行
CHANGE MASTER TO
MASTER_HOST='192.168.1.10',
MASTER_USER='repl',
MASTER_PASSWORD='repl_password',
MASTER_LOG_FILE='mysql-bin.000001',
MASTER_LOG_POS=154;
START SLAVE;
SHOW SLAVE STATUS\G;
关键检查点:
Slave_IO_Running: YesSlave_SQL_Running: YesSeconds_Behind_Master: 0
四、读写分离实现
4.1 方案对比
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 应用层代理 | 无单点、性能好 | 需要修改代码 | 中大型项目 |
| 中间件代理 | 透明接入 | 有性能损耗 | 快速接入 |
| 数据库中间件 | 支持分库分表 | 配置复杂 | 超大规模 |
4.2 Spring Boot实现
java
@Configuration
public class DataSourceConfig {
@Autowired
private DataSourceProperties properties;
@Bean
public DataSource dataSource() {
AbstractRoutingDataSource routingDataSource =
new AbstractRoutingDataSource() {
@Override
protected Object determineCurrentLookupKey() {
return RoutingContext.isWrite() ? "master" : "slave";
}
};
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put("master", createDataSource(properties.getMaster()));
targetDataSources.put("slave", createDataSource(properties.getSlave()));
routingDataSource.setTargetDataSources(targetDataSources);
return routingDataSource;
}
}
4.3 路由切粒
java
public class RoutingContext {
private static final ThreadLocal<Boolean> isWrite = new ThreadLocal<>();
public static void setWrite(boolean write) {
isWrite.set(write);
}
public static boolean isWrite() {
return Boolean.TRUE.equals(isWrite.get());
}
public static void clear() {
isWrite.remove();
}
}
Service层使用:
java
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
@Transactional
public void saveUser(User user) {
RoutingContext.setWrite(true);
userMapper.insert(user);
}
public User getUserById(Long id) {
RoutingContext.setWrite(false);
return userMapper.selectById(id);
}
}
五、常见问题与解决方案
5.1 主从延迟
症状: 从库延迟超过100ms
排查:
sql
SHOW SLAVE STATUS\G;
-- 查看 Seconds_Behind_Master
解决方案:
- 开启并行复制:
slave_parallel_workers > 0 - 优化主库大事务:分批提交
- 确认网络延迟
5.2 数据不一致
原因:
- 主从切换未同步
- 复制中断未处理
- 双写导致
修复方案:
bash
# 重新同步
mysqldump --master-data=2 -h master -u root -p > backup.sql
mysql -h slave -u root -p < backup.sql
5.3 relay log损坏
恢复步骤:
sql
STOP SLAVE;
RESET SLAVE;
CHANGE MASTER TO ...;
START SLAVE;
六、总结
核心要点:
- 主从复制基于binlog实现,有异步、半同步、同步三种模式
- 配置时注意server-id唯一,row格式binlog更安全
- 读写分离通过数据源路由实现,需处理事务边界
- 监控主从延迟,设置告警阈值
进阶方向:
- MGR(MySQL Group Replication)
- Binlog Server架构
- 分库分表中间件(ShardingSphere、MyCAT)