MySQL主从复制与读写分离实战指南

深入理解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 → 从库
  1. 主库执行SQL,生成binlog
  2. Binlog Dump线程读取binlog,发送给从库
  3. 从库IO线程接收,写入relay log
  4. 从库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: Yes
  • Slave_SQL_Running: Yes
  • Seconds_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;

六、总结

核心要点:

  1. 主从复制基于binlog实现,有异步、半同步、同步三种模式
  2. 配置时注意server-id唯一,row格式binlog更安全
  3. 读写分离通过数据源路由实现,需处理事务边界
  4. 监控主从延迟,设置告警阈值

进阶方向:

  • MGR(MySQL Group Replication)
  • Binlog Server架构
  • 分库分表中间件(ShardingSphere、MyCAT)
相关推荐
ly76892 小时前
MySQL 自增主键耗尽:从原理到应急恢复
数据库·mysql·innodb·故障恢复·自增主键·主键设计
文人sec2 小时前
MySQL主库出问题了,从库怎么办?备库为什么会延迟好几个小时?
android·数据库·mysql
hai_android3 小时前
不用 AIDL,手把手教你手写 Binder 代理类
android·kotlin
mmsx4 小时前
osmdroid 地图实战 03|地图的"分层世界":离线方案、图层模型与 Overlay 体系
android·前端
Java小白笔记4 小时前
MySQL 8.0 常用内置函数用例
数据库·mysql·mybatis
ZGG0034 小时前
MySQL 索引详解:B+ 树、聚簇索引与最左前缀
java·数据库·mysql
张文是假的啊5 小时前
IOC依赖注入问题:@Primary 和 @Qualifier的使用
android
换元不配限5 小时前
ConstraintLayout核心用法详解(三):引导线、屏障、组和占位
android·placeholder·barrier·约束布局·guideline
2301_800074216 小时前
mysql DQL
数据库·sql·mysql
心平气和量大福大6 小时前
android studio(AS)工具-调试-统计总行数
android·ide·android studio