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

本文详解MySQL主从复制原理与配置,以及读写分离的实现方案,从单机到高可用架构。

前言

单机MySQL的问题:

  • 单点故障
  • 读写压力集中
  • 无法水平扩展

主从复制是MySQL高可用的基础:

  • 数据冗余,提高可用性
  • 读写分离,提升性能
  • 实时备份,降低风险

今天来详解MySQL主从复制的实战配置。


一、主从复制原理

1.1 复制流程

复制代码
┌─────────────────────────────────────────────────────────┐
│                      Master                              │
│  ┌─────────┐    ┌─────────────┐                        │
│  │ 数据变更 │ →  │  Binlog     │                        │
│  └─────────┘    └──────┬──────┘                        │
└─────────────────────────┼────────────────────────────────┘
│ ① 传输binlog
↓
┌─────────────────────────────────────────────────────────┐
│                      Slave                               │
│  ┌─────────────┐    ┌─────────────┐    ┌─────────────┐ │
│  │  IO Thread  │ →  │ Relay Log   │ →  │ SQL Thread  │ │
│  │  接收binlog │    │  中继日志    │    │  回放执行   │ │
│  └─────────────┘    └─────────────┘    └─────────────┘ │
└─────────────────────────────────────────────────────────┘

1.2 复制模式

模式 说明 优缺点
异步复制 主库不等从库确认 性能好,可能丢数据
半同步 至少一个从库确认 折中方案
组复制(MGR) Paxos协议 强一致,复杂

二、环境准备

2.1 Docker Compose部署

yaml 复制代码
# docker-compose.yml
version: '3.8'

services:
  mysql-master:
    image: mysql:8.0
    container_name: mysql-master
    environment:
      MYSQL_ROOT_PASSWORD: root123
    ports:
      - "3306:3306"
    volumes:
      - ./master/conf:/etc/mysql/conf.d
      - ./master/data:/var/lib/mysql
      - ./master/logs:/var/log/mysql
    command: --server-id=1 --log-bin=mysql-bin --binlog-format=ROW

  mysql-slave:
    image: mysql:8.0
    container_name: mysql-slave
    environment:
      MYSQL_ROOT_PASSWORD: root123
    ports:
      - "3307:3306"
    volumes:
      - ./slave/conf:/etc/mysql/conf.d
      - ./slave/data:/var/lib/mysql
      - ./slave/logs:/var/log/mysql
    command: --server-id=2 --log-bin=mysql-bin --binlog-format=ROW --read-only=1
    depends_on:
      - mysql-master

2.2 配置文件

Master配置:

ini 复制代码
# master/conf/my.cnf
[mysqld]
server-id = 1
log-bin = mysql-bin
binlog-format = ROW
sync-binlog = 1

# 需要同步的数据库(不配则同步所有)
# binlog-do-db = mydb

# 忽略的数据库
binlog-ignore-db = mysql
binlog-ignore-db = information_schema
binlog-ignore-db = performance_schema
binlog-ignore-db = sys

# GTID模式(推荐)
gtid_mode = ON
enforce_gtid_consistency = ON

Slave配置:

ini 复制代码
# slave/conf/my.cnf
[mysqld]
server-id = 2
log-bin = mysql-bin
binlog-format = ROW
relay-log = relay-bin
read-only = 1

# GTID模式
gtid_mode = ON
enforce_gtid_consistency = ON

# 跳过某些错误(谨慎使用)
# slave-skip-errors = 1062

2.3 启动服务

bash 复制代码
# 创建目录
mkdir -p master/{conf,data,logs} slave/{conf,data,logs}

# 启动
docker compose up -d

# 查看状态
docker ps

三、配置主从复制

3.1 在Master创建复制用户

sql 复制代码
-- 连接Master
mysql -h 127.0.0.1 -P 3306 -uroot -proot123

-- 创建复制用户
CREATE USER 'repl'@'%' IDENTIFIED BY 'repl123';
GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'repl'@'%';
FLUSH PRIVILEGES;

-- 查看Master状态
SHOW MASTER STATUS;
+------------------+----------+--------------+------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+------------------+----------+--------------+------------------+
| mysql-bin.000003 |      857 |              | mysql,...        |
+------------------+----------+--------------+------------------+

3.2 配置Slave

sql 复制代码
-- 连接Slave
mysql -h 127.0.0.1 -P 3307 -uroot -proot123

-- 方式1:传统位点复制
CHANGE MASTER TO
    MASTER_HOST='mysql-master',
    MASTER_PORT=3306,
    MASTER_USER='repl',
    MASTER_PASSWORD='repl123',
    MASTER_LOG_FILE='mysql-bin.000003',
    MASTER_LOG_POS=857;

-- 方式2:GTID复制(推荐)
CHANGE MASTER TO
    MASTER_HOST='mysql-master',
    MASTER_PORT=3306,
    MASTER_USER='repl',
    MASTER_PASSWORD='repl123',
    MASTER_AUTO_POSITION=1;

-- 启动复制
START SLAVE;

-- 查看复制状态
SHOW SLAVE STATUS\G

3.3 验证复制

sql 复制代码
-- 关键字段
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Seconds_Behind_Master: 0

-- Master写入数据
CREATE DATABASE testdb;
USE testdb;
CREATE TABLE users (id INT PRIMARY KEY, name VARCHAR(50));
INSERT INTO users VALUES (1, 'test');

-- Slave查询验证
USE testdb;
SELECT * FROM users;
+----+------+
| id | name |
+----+------+
|  1 | test |
+----+------+

四、读写分离

4.1 方案对比

方案 优点 缺点
代码层面 简单,无额外组件 代码侵入
中间件 透明,功能丰富 增加组件
MySQL Router 官方支持 功能有限

4.2 代码层实现(Spring Boot)

java 复制代码
// 数据源配置
@Configuration
public class DataSourceConfig {
    
    @Bean
    @ConfigurationProperties("spring.datasource.master")
    public DataSource masterDataSource() {
        return DataSourceBuilder.create().build();
    }
    
    @Bean
    @ConfigurationProperties("spring.datasource.slave")
    public DataSource slaveDataSource() {
        return DataSourceBuilder.create().build();
    }
    
    @Bean
    public DataSource routingDataSource(
            @Qualifier("masterDataSource") DataSource master,
            @Qualifier("slaveDataSource") DataSource slave) {
        
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put("master", master);
        targetDataSources.put("slave", slave);
        
        RoutingDataSource routing = new RoutingDataSource();
        routing.setTargetDataSources(targetDataSources);
        routing.setDefaultTargetDataSource(master);
        return routing;
    }
}

// 动态数据源
public class RoutingDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceContextHolder.getDataSource();
    }
}

// 数据源上下文
public class DataSourceContextHolder {
    private static final ThreadLocal<String> CONTEXT = new ThreadLocal<>();
    
    public static void setMaster() { CONTEXT.set("master"); }
    public static void setSlave() { CONTEXT.set("slave"); }
    public static String getDataSource() { return CONTEXT.get(); }
    public static void clear() { CONTEXT.remove(); }
}

// AOP切面
@Aspect
@Component
public class DataSourceAspect {
    
    @Before("@annotation(readOnly)")
    public void setReadDataSource(ReadOnly readOnly) {
        DataSourceContextHolder.setSlave();
    }
    
    @Before("@annotation(org.springframework.transaction.annotation.Transactional)")
    public void setWriteDataSource() {
        DataSourceContextHolder.setMaster();
    }
    
    @After("execution(* com.example.service.*.*(..))")
    public void clear() {
        DataSourceContextHolder.clear();
    }
}

4.3 中间件方案(ShardingSphere)

yaml 复制代码
# application.yml
spring:
  shardingsphere:
    datasource:
      names: master,slave
      master:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://192.168.1.1:3306/mydb
        username: root
        password: root123
      slave:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://192.168.1.2:3306/mydb
        username: root
        password: root123
    rules:
      readwrite-splitting:
        data-sources:
          readwrite_ds:
            static-strategy:
              write-data-source-name: master
              read-data-source-names: slave
            load-balancer-name: round_robin
        load-balancers:
          round_robin:
            type: ROUND_ROBIN

五、高可用架构

5.1 MHA架构

复制代码
┌─────────────────────────────────────────────────────┐
│                    MHA Manager                       │
│                   (监控+故障转移)                    │
└─────────────────────────────────────────────────────┘
        ↓               ↓               ↓
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│   Master     │ │   Slave1     │ │   Slave2     │
│ (可写)       │ │ (候选Master) │ │ (只读)       │
└──────────────┘ └──────────────┘ └──────────────┘

5.2 MGR组复制

sql 复制代码
-- 所有节点配置
[mysqld]
server_id = 1
gtid_mode = ON
enforce_gtid_consistency = ON
binlog_checksum = NONE

# 组复制配置
plugin_load_add = 'group_replication.so'
group_replication_group_name = "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
group_replication_start_on_boot = OFF
group_replication_local_address = "192.168.1.1:33061"
group_replication_group_seeds = "192.168.1.1:33061,192.168.1.2:33061,192.168.1.3:33061"
group_replication_bootstrap_group = OFF

六、跨机房部署

6.1 场景挑战

复制代码
需求:
- 主库在总部机房
- 从库在分部机房(异地灾备)
- 两个机房网络不通

传统方案:
- 专线:成本高
- 公网暴露MySQL端口:风险大

6.2 组网方案

使用组网软件(如星空组网)打通网络:

复制代码
┌─────────────────────────────────────────────────────────┐
│                    组网虚拟局域网                         │
│                                                          │
│  ┌──────────────────┐      ┌──────────────────┐         │
│  │     总部机房      │      │     分部机房      │         │
│  │                  │      │                  │         │
│  │  Master          │      │  Slave           │         │
│  │  10.10.0.1:3306  │ ←同步─│  10.10.0.2:3306  │         │
│  │                  │      │                  │         │
│  └──────────────────┘      └──────────────────┘         │
│                                                          │
└─────────────────────────────────────────────────────────┘

Slave配置:

sql 复制代码
-- 使用组网IP连接Master
CHANGE MASTER TO
    MASTER_HOST='10.10.0.1',   -- 组网IP
    MASTER_PORT=3306,
    MASTER_USER='repl',
    MASTER_PASSWORD='repl123',
    MASTER_AUTO_POSITION=1;
    
START SLAVE;

优势:

  • 不需要公网暴露3306端口
  • 加密传输,安全可靠
  • 配置简单
  • 运维人员可通过组网远程管理

6.3 远程运维

bash 复制代码
# 通过组网IP远程连接
mysql -h 10.10.0.1 -P 3306 -uroot -p

# 远程备份
mysqldump -h 10.10.0.1 -uroot -p mydb > backup.sql

# 远程监控
mysqlsh --uri root@10.10.0.1:3306 --js

七、监控与运维

7.1 监控指标

sql 复制代码
-- 复制延迟
SHOW SLAVE STATUS\G
-- Seconds_Behind_Master

-- 线程状态
SHOW PROCESSLIST;

-- 复制错误
SHOW SLAVE STATUS\G
-- Last_Error, Last_IO_Error, Last_SQL_Error

7.2 监控脚本

bash 复制代码
#!/bin/bash
# check_replication.sh

MYSQL_CMD="mysql -h 127.0.0.1 -P 3307 -urepl -prepl123"

IO_RUNNING=$($MYSQL_CMD -e "SHOW SLAVE STATUS\G" | grep "Slave_IO_Running" | awk '{print $2}')
SQL_RUNNING=$($MYSQL_CMD -e "SHOW SLAVE STATUS\G" | grep "Slave_SQL_Running" | awk '{print $2}')
DELAY=$($MYSQL_CMD -e "SHOW SLAVE STATUS\G" | grep "Seconds_Behind_Master" | awk '{print $2}')

echo "IO Thread: $IO_RUNNING"
echo "SQL Thread: $SQL_RUNNING"
echo "Delay: ${DELAY}s"

if [ "$IO_RUNNING" != "Yes" ] || [ "$SQL_RUNNING" != "Yes" ]; then
    echo "ALERT: Replication is broken!"
    # 发送告警
fi

if [ "$DELAY" -gt 60 ]; then
    echo "ALERT: Replication delay > 60s"
fi

7.3 常见问题处理

复制中断:

sql 复制代码
-- 查看错误
SHOW SLAVE STATUS\G

-- 跳过错误(谨慎)
STOP SLAVE;
SET GLOBAL SQL_SLAVE_SKIP_COUNTER = 1;
START SLAVE;

-- 或使用GTID跳过
SET GTID_NEXT='xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx:N';
BEGIN; COMMIT;
SET GTID_NEXT='AUTOMATIC';
START SLAVE;

主从切换:

sql 复制代码
-- 原Slave提升为Master
STOP SLAVE;
RESET SLAVE ALL;
SET GLOBAL read_only = 0;

-- 原Master降为Slave
CHANGE MASTER TO ...;
SET GLOBAL read_only = 1;
START SLAVE;

八、性能优化

8.1 并行复制

ini 复制代码
# MySQL 5.7+
slave_parallel_type = LOGICAL_CLOCK
slave_parallel_workers = 4
slave_preserve_commit_order = 1

8.2 半同步复制

sql 复制代码
-- Master
INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so';
SET GLOBAL rpl_semi_sync_master_enabled = 1;
SET GLOBAL rpl_semi_sync_master_timeout = 10000;

-- Slave
INSTALL PLUGIN rpl_semi_sync_slave SONAME 'semisync_slave.so';
SET GLOBAL rpl_semi_sync_slave_enabled = 1;
STOP SLAVE; START SLAVE;

8.3 参数优化

ini 复制代码
# binlog优化
binlog_cache_size = 4M
max_binlog_size = 500M
expire_logs_days = 7

# 复制优化
slave_net_timeout = 60
sync_relay_log = 10000
relay_log_recovery = 1

九、总结

MySQL主从复制要点:

  1. 基础配置:server-id唯一,开启binlog
  2. GTID模式:推荐使用,简化管理
  3. 读写分离:中间件方案更优雅
  4. 高可用:MHA/MGR实现自动故障转移
  5. 跨机房:组网打通后正常同步
  6. 监控告警:复制状态和延迟

生产环境清单:

复制代码
☑ 主从复制配置完成
☑ 复制用户权限最小化
☑ 监控脚本部署
☑ 备份策略制定
☑ 故障切换演练

参考资料

  1. MySQL官方复制文档:https://dev.mysql.com/doc/refman/8.0/en/replication.html
  2. MySQL高可用:https://dev.mysql.com/doc/mysql-ha-scalability/en/

💡 建议:生产环境务必使用GTID模式,配置半同步复制,定期进行主从切换演练。

相关推荐
一水鉴天3 小时前
整体设计 定稿 之 5 讨论问题汇总 和新建 表述总表/项目结构表 文档分析,到读表工具核心设计讨论(豆包助手)
数据库·人工智能·重构
我科绝伦(Huanhuan Zhou)3 小时前
Linux 环境下 SQL Server 自动收缩日志作业创建脚本(Shell 版)
linux·运维·数据库·sql server
q_19132846953 小时前
基于SpringBoot2+Vue2的装修报价网站
java·vue.js·spring boot·mysql·计算机毕业设计·演示文稿
TDengine (老段)3 小时前
山东港口科技借助 TDengine 构建智慧港口“数据基石”
大数据·数据库·物联网·时序数据库·tdengine
Hello.Reader3 小时前
Flink SQL INSERT 语句单表写入、多表分流、分区覆盖与 StatementSet
数据库·sql·flink
马克学长3 小时前
SSM小型餐饮综合管理系统j1c7m(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面
数据库·ssm 框架·小型餐饮管理系统·菜品管理·员工考勤
葱卤山猪4 小时前
【Qt】心跳检测与粘包处理:打造稳定可靠的TCP Socket通信
开发语言·数据库·qt
qq_12498707534 小时前
基于Spring Boot的社区医院管理系统的设计与实现(源码+论文+部署+安装)
java·数据库·人工智能·spring boot·毕业设计