MySQL主从复制通过将主库的数据变更同步到多个从库,实现数据冗余和读写分离的基础架构,搭配负载均衡可以合理分配数据库请求,避免单节点压力过大,提升整体服务的稳定性和吞吐量。

MySQL主从复制基础原理
主从复制的核心流程分为三个步骤:主库将数据的变更记录到二进制日志(binlog)中,从库的I/O线程拉取主库的binlog并写入本地的中继日志(relay log),从库的SQL线程读取中继日志执行变更操作,最终实现从库数据和主库一致。
主从复制默认是异步的,主库写入成功后立即返回,不等待从库同步完成,因此从库可能存在短暂的数据延迟,这是搭配负载均衡时需要考虑的重要因素。
基于应用层实现的负载均衡方案
应用层负载均衡是指在业务代码中直接区分读写请求,将写请求发送到主库,读请求按照规则分配到各个从库,这种方式实现简单,不需要额外的中间件。
实现逻辑
可以在数据访问层封装数据库操作入口,通过方法名或者注解标记操作类型,比如所有以select开头的查询方法走从库,insert、update、delete操作走主库。
代码示例(Java + Spring JDBC)
import org.springframework.jdbc.core.JdbcTemplate;
import java.util.List;
public class UserDao {
// 主库JdbcTemplate,处理写操作
private JdbcTemplate masterJdbcTemplate;
// 从库JdbcTemplate列表,处理读操作
private List<JdbcTemplate> slaveJdbcTemplateList;
// 从库索引,用于轮询分配
private int slaveIndex = 0;
// 写操作:插入用户
public int insertUser(String name, int age) {
String sql = "INSERT INTO user (name, age) VALUES (?, ?)";
return masterJdbcTemplate.update(sql, name, age);
}
// 读操作:查询所有用户,轮询分配到从库
public List<User> selectAllUsers() {
// 简单轮询负载均衡
JdbcTemplate slaveJdbcTemplate = slaveJdbcTemplateList.get(slaveIndex % slaveJdbcTemplateList.size());
slaveIndex++;
String sql = "SELECT id, name, age FROM user";
return slaveJdbcTemplate.query(sql, (rs, rowNum) -> {
User user = new User();
user.setId(rs.getInt("id"));
user.setName(rs.getString("name"));
user.setAge(rs.getInt("age"));
return user;
});
}
// setter方法省略
}
优缺点分析
- 优点:无需额外中间件,架构简单,性能损耗小,可控性高。
- 缺点:负载均衡逻辑耦合在业务代码中,后续调整规则需要修改代码,从库扩容时需要修改配置并重启服务。
基于中间件实现的负载均衡方案
使用专业的数据库中间件来管理主从复制的负载均衡,业务代码只需要连接中间件,不需要关心主从节点的分配逻辑,是更常用的生产环境方案。
常见中间件对比
| 中间件名称 | 核心功能 | 适用场景 |
|---|---|---|
| MySQL Router | 官方提供的中间件,支持读写分离、故障自动切换 | 小规模MySQL集群,对官方组件依赖度高的场景 |
| ProxySQL | 高性能MySQL代理,支持动态配置、查询缓存、连接池管理 | 中大规模集群,需要灵活负载均衡规则的场景 |
| MyCat | 基于Java的数据库中间件,支持分库分表、读写分离 | 需要同时做数据分片的业务场景 |
ProxySQL配置示例
以下是ProxySQL实现MySQL主从复制负载均衡的基础配置步骤:
1. 添加主从节点到ProxySQL的服务器列表:
-- 添加主库 INSERT INTO mysql_servers (hostgroup_id, hostname, port, weight) VALUES (1, '192.168.0.1', 3306, 100); -- 添加从库,weight表示权重,值越高分配的请求越多 INSERT INTO mysql_servers (hostgroup_id, hostname, port, weight) VALUES (2, '192.168.0.2', 3306, 50); INSERT INTO mysql_servers (hostgroup_id, hostname, port, weight) VALUES (2, '192.168.0.3', 3306, 50); -- 加载配置到运行时并保存到磁盘 LOAD MYSQL SERVERS TO RUNTIME; SAVE MYSQL SERVERS TO DISK;
2. 配置读写分离规则,将写请求路由到主库组,读请求路由到从库组:
-- 写请求规则:匹配所有非SELECT开头的语句,路由到hostgroup_id=1的主库组 INSERT INTO mysql_query_rules (rule_id, active, match_digest, destination_hostgroup, apply) VALUES (1, 1, '^(?!SELECT).*', 1, 1); -- 读请求规则:匹配SELECT语句,路由到hostgroup_id=2的从库组 INSERT INTO mysql_query_rules (rule_id, active, match_digest, destination_hostgroup, apply) VALUES (2, 1, '^SELECT.*', 2, 1); -- 加载规则到运行时并保存 LOAD MYSQL QUERY RULES TO RUNTIME; SAVE MYSQL QUERY RULES TO DISK;
负载均衡的注意事项
- 主从延迟问题:如果从库同步延迟较高,读请求分配到延迟从库可能会读到旧数据,可以通过监控从库延迟,将延迟超过阈值的从库暂时移出负载均衡列表。
- 事务中的读请求:同一个事务中的读请求如果分配到从库,可能会因为事务未提交导致主库变更未同步到从库,因此建议事务中的读请求也走主库。
- 从库健康检查:需要定期检测从库的状态,自动剔除故障节点,避免请求分配到不可用的从库。
- 权重调整:可以根据从库的性能配置不同的权重,性能更好的从库分配更多的读请求,充分利用硬件资源。
总结
MySQL主从复制搭配负载均衡是提升数据库性能的有效方案,小规模场景可以选择应用层实现,成本低且可控性高;生产环境更推荐使用ProxySQL等专业中间件,降低业务耦合度,支持更灵活的负载均衡规则。实际落地时需要根据业务的读写比例、数据一致性要求选择合适的方案,并做好延迟监控和故障转移机制,保障数据库服务的稳定运行。