MySQL 主从复制与读写分离:从原理到实战全解析

一、前言

在数据规模爆炸式增长的今天,数据库系统的扩展性和高可用性已成为企业架构设计的核心命题。传统单节点 MySQL 架构在面对高并发读写、数据容灾及性能瓶颈时显得力不从心,而 MySQL 主从复制通过实时数据同步机制,为主库分担读压力并提供灾备支持;MyCat 中间件则基于主从架构实现透明化读写分离,进一步释放系统资源、提升吞吐量。

本文将围绕这两大核心技术展开,通过原理讲解、配置实战与案例剖析,帮助读者系统掌握数据库高可用架构设计思路,实现从基础配置到性能调优的全链路技能提升。


二、案例分析

2.1 案例概述

某电商平台在业务高速发展期,面临以下核心问题:

  • 读写压力失衡:单节点 MySQL 承载了所有读写请求,读请求占比高达 80%,导致写操作延迟严重,影响订单创建、支付等核心流程。
  • 数据容灾薄弱:单节点故障会导致服务完全中断,无法满足 7×24 小时高可用要求。
  • 扩展性不足:垂直扩容(升级硬件)成本高昂且存在性能天花板,无法快速应对业务峰值。

为解决上述问题,架构团队设计了MySQL 主从复制 + MyCat 读写分离的解决方案:

  1. 搭建一主多从的 MySQL 集群,主库负责写操作,从库同步主库数据并承载读请求。
  2. 引入 MyCat 中间件,实现读写请求的透明路由,应用层无需感知底层数据库集群结构。
  3. 实现数据冗余备份,主库故障时可快速切换从库为新主库,保障业务连续性。

2.2 核心技术知识点

本案例的核心技术围绕两大模块展开:MySQL 主从复制读写分离,两者相辅相成,共同构建高可用、高性能的数据库架构。


三、MySQL 主从复制原理

3.1 核心原理

MySQL 主从复制(Master-Slave Replication)是指将主数据库(Master)的 DDL、DML 操作通过二进制日志(Binary Log)同步到从数据库(Slave),并在从库上重放这些日志,从而保持主从数据一致的技术。

其核心流程基于异步复制模型,主要包含三个关键角色:

  • 主库(Master):接收写请求,记录所有数据变更到二进制日志(Binary Log)。
  • 从库(Slave):从主库拉取二进制日志,写入本地中继日志(Relay Log),并重放日志以同步数据。
  • 日志同步线程 :主库的Binlog Dump Thread、从库的I/O ThreadSQL Thread协同完成日志传输与重放。

3.2 复制的工作流程

主从复制的完整流程可分为以下 5 个步骤:

  1. 主库记录变更:当主库执行 INSERT/UPDATE/DELETE 等写操作时,会将数据变更记录到二进制日志(Binary Log)中,同时标记日志位置。
  2. 从库发起连接 :从库启动后,I/O Thread向主库发起连接,请求从指定日志位置开始同步数据。
  3. 主库推送日志 :主库的Binlog Dump Thread检测到从库的连接请求后,会读取 Binary Log 中指定位置之后的日志内容,推送给从库的I/O Thread
  4. 从库写入中继日志 :从库的I/O Thread接收日志后,将其写入本地中继日志(Relay Log),并记录当前同步到的日志位置。
  5. 从库重放日志 :从库的SQL Thread读取 Relay Log,解析并重放其中的 SQL 语句,将数据变更应用到本地数据库,最终实现与主库的数据一致。

3.3 复制模式分类

MySQL 主从复制支持多种模式,不同模式在数据一致性、性能和延迟上各有侧重:

  1. 异步复制(Asynchronous Replication)

    • 原理:主库执行完写操作后,立即返回结果给客户端,不等待从库同步完成。
    • 优点:性能极高,主库写操作延迟小。
    • 缺点:主库故障时可能存在数据丢失风险(从库未同步完成)。
    • 适用场景:对性能要求高、数据一致性要求相对较低的业务(如日志存储、非核心业务)。
  2. 半同步复制(Semi-Synchronous Replication)

    • 原理:主库执行完写操作后,需等待至少一个从库确认收到日志后,才返回结果给客户端。
    • 优点:降低数据丢失风险,平衡性能与一致性。
    • 缺点:写操作延迟略高于异步复制,依赖从库响应速度。
    • 适用场景:核心业务(如订单、支付),需保障数据一致性的场景。
  3. 组复制(Group Replication)

    • 原理:基于 Paxos 协议实现的多主复制模式,多个节点组成复制组,所有节点可同时处理写操作,通过共识机制保证数据一致性。
    • 优点:高可用、高扩展,支持多写架构,自动故障转移。
    • 缺点:配置复杂,性能开销较大,对网络稳定性要求高。
    • 适用场景:金融、政务等对数据一致性和高可用要求极高的核心系统。

四、MySQL 读写分离原理

4.1 核心原理

读写分离(Read-Write Splitting)是基于主从复制架构,将写请求路由到主库读请求路由到从库的负载均衡策略,从而实现读写请求的解耦,提升系统整体吞吐量。

其核心逻辑是:利用主从复制保证数据一致性,通过中间件或应用层代码实现请求路由,让主库专注处理写操作,从库集群承载大部分读请求,避免单节点资源耗尽。

4.2 读写分离的实现方式

目前主流的读写分离实现方式分为两类:

4.2.1 应用层实现

在应用代码中手动维护主从数据源,根据 SQL 类型(INSERT/UPDATE/DELETE vs SELECT)路由到不同数据库节点。

  • 优点:灵活可控,无需引入额外中间件,性能损耗小。
  • 缺点:代码侵入性强,需手动处理事务、负载均衡、故障转移等逻辑,维护成本高。
  • 典型实现:基于 Spring 的 AbstractRoutingDataSource 实现动态数据源切换。
4.2.2 中间件实现

通过数据库中间件(如 MyCat、Sharding-JDBC、MaxScale 等)实现透明化读写分离,应用层无需感知底层数据库结构。

  • 优点:代码无侵入,支持负载均衡、故障自动转移、读写权重调整等高级功能,扩展性强。
  • 缺点:引入额外组件,增加架构复杂度,需维护中间件服务。
  • 典型实现:MyCat、Sharding-JDBC、MySQL Router、MaxScale。

4.3 读写分离的核心挑战

  1. 数据一致性延迟:主从复制存在延迟,写操作后立即读取可能读到旧数据,需通过业务逻辑(如缓存、读主库)或中间件(如强制读主)解决。
  2. 事务处理:跨节点事务需保证 ACID 特性,分布式事务(如 XA、TCC)实现复杂,性能损耗较大。
  3. 从库负载均衡:需合理分配读请求到多个从库,避免单个从库过载,常用策略包括轮询、权重、最小连接数等。
  4. 故障转移:主库或从库故障时,需快速切换节点,保障服务可用性,需配合监控工具(如 Prometheus、Zabbix)实现自动告警与切换。

五、实战搭建:MySQL 主从复制

5.1 环境准备

本案例采用一主一从架构,操作系统为 CentOS 7,MySQL 版本为 8.0.36,服务器信息如下:

表格

角色 IP 地址 端口 备注
Master 192.168.10.101 3306 主库,处理写请求
Slave 192.168.10.102 3306 从库,处理读请求
5.1.1 基础环境配置
  1. 关闭防火墙与 SELinux:

    复制代码
    systemctl stop firewalld
    systemctl disable firewalld
    sed -i 's/SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config
    setenforce 0
  2. 配置主机名与 hosts 解析:

    复制代码
    # Master节点
    hostnamectl set-hostname mysql-master
    # Slave节点
    hostnamectl set-hostname mysql-slave
    # 两台节点均执行
    echo "192.168.10.101 mysql-master" >> /etc/hosts
    echo "192.168.10.102 mysql-slave" >> /etc/hosts
  3. 安装依赖包:

    复制代码
    yum install -y libaio-devel numactl-devel
5.1.2 二进制安装 MySQL
  1. 下载 MySQL 8.0.36 二进制包:

    复制代码
    wget https://cdn.mysql.com/Downloads/MySQL-8.0/mysql-8.0.36-linux-glibc2.28-x86_64.tar.xz
  2. 解压并创建软链接:

    复制代码
    tar -xvf mysql-8.0.36-linux-glibc2.28-x86_64.tar.xz -C /usr/local/
    ln -s /usr/local/mysql-8.0.36-linux-glibc2.28-x86_64 /usr/local/mysql
  3. 创建 mysql 用户与数据目录:

    复制代码
    useradd -r -s /sbin/nologin mysql
    mkdir -p /data/mysql
    chown -R mysql:mysql /data/mysql
  4. 初始化 MySQL:

    复制代码
    /usr/local/mysql/bin/mysqld --initialize --user=mysql --datadir=/data/mysql

    初始化完成后,会生成临时 root 密码,需记录备用。

  5. 配置系统服务:

    复制代码
    cp /usr/local/mysql/support-files/mysql.server /etc/init.d/mysqld
    chmod +x /etc/init.d/mysqld
    systemctl daemon-reload
    systemctl enable mysqld

5.2 配置 Master 主服务器

5.2.1 修改 MySQL 配置文件

编辑/etc/my.cnf,添加以下配置:

复制代码
[mysqld]
# 基础配置
datadir=/data/mysql
socket=/tmp/mysql.sock
pid-file=/data/mysql/mysqld.pid
user=mysql
port=3306
bind-address=0.0.0.0

# 主从复制核心配置
server-id=1  # 唯一ID,主从节点不能重复
log-bin=mysql-bin  # 开启二进制日志
binlog_format=ROW  # 推荐使用ROW格式,数据一致性更高
binlog-do-db=testdb  # 需要同步的数据库(可选,不配置则同步所有库)
binlog-ignore-db=mysql  # 忽略同步的系统库
expire_logs_days=7  # 二进制日志自动清理时间
5.2.2 启动 Master 服务并创建复制用户
  1. 启动 MySQL 服务:

    复制代码
    systemctl start mysqld
  2. 登录 MySQL 并修改 root 密码:

    复制代码
    /usr/local/mysql/bin/mysql -uroot -p'临时密码'
    ALTER USER 'root'@'localhost' IDENTIFIED BY '新密码';
    FLUSH PRIVILEGES;
  3. 创建用于主从复制的用户:

    复制代码
    CREATE USER 'repl'@'192.168.10.%' IDENTIFIED BY 'Repl@123';
    GRANT REPLICATION SLAVE ON *.* TO 'repl'@'192.168.10.%';
    FLUSH PRIVILEGES;
  4. 查看 Master 状态,记录二进制日志文件名与位置:

    复制代码
    SHOW MASTER STATUS;

    输出示例:

    复制代码
    +------------------+----------+--------------+------------------+-------------------+
    | File             | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
    +------------------+----------+--------------+------------------+-------------------+
    | mysql-bin.000001 |      156 | testdb       | mysql            |                   |
    +------------------+----------+--------------+------------------+-------------------+

5.3 配置 Slave 从服务器

5.3.1 修改 MySQL 配置文件

编辑/etc/my.cnf,添加以下配置:

复制代码
[mysqld]
# 基础配置
datadir=/data/mysql
socket=/tmp/mysql.sock
pid-file=/data/mysql/mysqld.pid
user=mysql
port=3306
bind-address=0.0.0.0

# 主从复制核心配置
server-id=2  # 唯一ID,与Master不同
relay-log=mysql-relay-bin  # 开启中继日志
read_only=1  # 从库设置为只读(可选,防止误写)
super_read_only=1  # 8.0版本推荐,限制超级用户写入
5.3.2 启动 Slave 服务并关联 Master
  1. 启动 MySQL 服务:

    复制代码
    systemctl start mysqld
  2. 登录 MySQL 并修改 root 密码(同 Master 步骤)。

  3. 配置 Slave 连接 Master 的信息:

    复制代码
    CHANGE MASTER TO
    MASTER_HOST='192.168.10.101',
    MASTER_USER='repl',
    MASTER_PASSWORD='Repl@123',
    MASTER_LOG_FILE='mysql-bin.000001',  # 对应Master的File字段
    MASTER_LOG_POS=156;  # 对应Master的Position字段
  4. 启动 Slave 复制线程:

    复制代码
    START SLAVE;
  5. 查看 Slave 状态,验证复制是否正常:

    复制代码
    SHOW SLAVE STATUS\G

    关键指标:

    • Slave_IO_Running: Yes
    • Slave_SQL_Running: Yes若两个字段均为Yes,则主从复制配置成功。

5.4 主从复制效果验证

  1. 在 Master 上创建数据库与表:

    复制代码
    CREATE DATABASE testdb;
    USE testdb;
    CREATE TABLE user (id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(20));
    INSERT INTO user (name) VALUES ('张三'), ('李四');
  2. 在 Slave 上查询数据:

    复制代码
    USE testdb;
    SELECT * FROM user;

    若能查询到 Master 插入的数据,则说明主从复制正常工作。


六、实战搭建:基于 MyCat 的读写分离

6.1 MyCat 简介

MyCat 是一款开源的分布式数据库中间件,基于 Java 开发,支持 MySQL、Oracle、SQL Server 等多种数据库,核心功能包括分库分表、读写分离、负载均衡、故障转移等。

本案例中,MyCat 作为应用层与 MySQL 集群之间的中间层,实现读写请求的透明路由:写请求自动转发到 Master,读请求按权重分配到 Slave,应用层只需连接 MyCat,无需关心底层数据库结构。

6.2 MyCat 安装与配置

6.2.1 环境准备

MyCat 依赖 Java 环境,需先安装 JDK 1.8 或以上版本:

复制代码
yum install -y java-1.8.0-openjdk-devel

验证 Java 版本:

复制代码
java -version
6.2.2 安装 MyCat
  1. 下载 MyCat 1.6.7.6 版本:

    复制代码
    wget http://dl.mycat.org.cn/1.6.7.6/Mycat-server-1.6.7.6-release-20221028-linux.tar.gz
  2. 解压并创建软链接:

    复制代码
    tar -zxvf Mycat-server-1.6.7.6-release-20221028-linux.tar.gz -C /usr/local/
    ln -s /usr/local/mycat /usr/local/mycat
  3. 配置环境变量:

    复制代码
    echo "export MYCAT_HOME=/usr/local/mycat" >> /etc/profile
    echo "export PATH=\$PATH:\$MYCAT_HOME/bin" >> /etc/profile
    source /etc/profile
6.2.3 核心配置文件

MyCat 的核心配置文件位于/usr/local/mycat/conf目录下,主要包括:

  • server.xml:配置 MyCat 服务、用户权限、逻辑库等。
  • schema.xml:配置逻辑库、逻辑表、数据节点与物理数据库映射。
  • rule.xml:配置分库分表规则(本案例无需分库分表,可忽略)。
6.2.3.1 配置 server.xml

编辑server.xml,添加用户与逻辑库配置:

复制代码
<user name="root">
    <property name="password">Root@123</property>
    <property name="schemas">TESTDB</property>  <!-- 逻辑库名,需与schema.xml一致 -->
</user>
<user name="user">
    <property name="password">User@123</property>
    <property name="schemas">TESTDB</property>
    <property name="readOnly">true</property>  <!-- 只读用户 -->
</user>
6.2.3.2 配置 schema.xml

编辑schema.xml,配置逻辑库与读写分离规则:

复制代码
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
    <!-- 逻辑库配置 -->
    <schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100">
        <!-- 逻辑表,关联数据节点 -->
        <table name="user" dataNode="dn1" />
    </schema>

    <!-- 数据节点,关联物理数据库 -->
    <dataNode name="dn1" dataHost="localhost1" database="testdb" />

    <!-- 物理数据库主机配置,实现读写分离 -->
    <dataHost name="localhost1" maxCon="1000" minCon="10" balance="1"
              writeType="0" dbType="mysql" dbDriver="native" switchType="1"  slaveThreshold="100">
        <heartbeat>select user()</heartbeat>
        <!-- 写节点:Master -->
        <writeHost host="hostM1" url="192.168.10.101:3306" user="root" password="Root@123">
            <!-- 读节点:Slave -->
            <readHost host="hostS1" url="192.168.10.102:3306" user="root" password="Root@123" weight="1" />
        </writeHost>
    </dataHost>
</mycat:schema>

关键参数说明:

  • balance="1":开启读写分离,读请求负载均衡到所有 readHost。
  • writeType="0":写请求只路由到第一个 writeHost。
  • switchType="1":自动切换,主库故障时自动切换到从库。
  • weight="1":读节点权重,可调整多个从库的负载比例。

6.3 启动 MyCat 并验证读写分离

6.3.1 启动 MyCat 服务
复制代码
mycat start

查看启动日志:

复制代码
tail -f /usr/local/mycat/logs/mycat.log

若日志中出现MyCat startup successfully,则启动成功。

6.3.2 验证读写分离
  1. 连接 MyCat(端口默认 8066):

    复制代码
    mysql -uroot -pRoot@123 -h127.0.0.1 -P8066 -DTESTDB
  2. 执行写操作(路由到 Master):

    复制代码
    INSERT INTO user (name) VALUES ('王五');
  3. 执行读操作(路由到 Slave):

    复制代码
    SELECT * FROM user;
  4. 验证路由效果:

    • 查看 Master 的二进制日志:SHOW BINLOG EVENTS;,可看到写操作记录。
    • 查看 Slave 的中继日志:SHOW RELAYLOG EVENTS;,可看到读操作未记录(仅同步写操作)。
    • 查看 MyCat 日志:/usr/local/mycat/logs/mycat.log,可看到读写请求的路由信息。

6.4 读写分离高级配置

6.4.1 多从库负载均衡

若存在多个从库,可在schema.xml中配置多个readHost,并设置不同权重:

复制代码
<writeHost host="hostM1" url="192.168.10.101:3306" user="root" password="Root@123">
    <readHost host="hostS1" url="192.168.10.102:3306" user="root" password="Root@123" weight="2" />
    <readHost host="hostS2" url="192.168.10.103:3306" user="root" password="Root@123" weight="1" />
</writeHost>

权重越大,分配的读请求越多(示例中 hostS1 承担 2/3 读请求,hostS2 承担 1/3)。

6.4.2 强制读主库

对于实时性要求高的读请求(如刚写完立即查询),可在 SQL 中添加/*#mycat:db_type=master*/注释,强制路由到主库:

复制代码
/*#mycat:db_type=master*/ SELECT * FROM user WHERE id=3;

七、常见问题与解决方案

7.1 主从复制常见问题

7.1.1 Slave_IO_Running: No

原因

  • Master 与 Slave 网络不通。
  • 复制用户权限不足或密码错误。
  • Master 的二进制日志文件或位置配置错误。
  • Slave 的 server-id 与 Master 重复。

解决方案

  1. 检查网络连通性:ping Master_IPtelnet Master_IP 3306
  2. 验证复制用户权限:SHOW GRANTS FOR 'repl'@'192.168.10.%';
  3. 核对CHANGE MASTER TO中的MASTER_LOG_FILEMASTER_LOG_POS
  4. 检查server-id是否唯一。
7.1.2 Slave_SQL_Running: No

原因

  • Slave 与 Master 数据不一致(如手动修改 Slave 数据)。
  • 主库执行了从库无法重放的 SQL(如 DROP TABLE 不存在的表)。
  • 主键冲突、外键约束等 SQL 执行错误。

解决方案

  1. 查看错误日志:SHOW SLAVE STATUS\G中的Last_SQL_Error字段。

  2. 跳过错误事务(谨慎使用,可能导致数据不一致):

    复制代码
    STOP SLAVE;
    SET GLOBAL SQL_SLAVE_SKIP_COUNTER=1;
    START SLAVE;
  3. 重新初始化 Slave:备份 Master 数据,导入 Slave 后重新配置复制。

7.2 读写分离常见问题

7.2.1 读请求路由到主库

原因

  • MyCat 配置balance参数错误(如balance="0")。
  • 事务中包含写操作,后续读请求默认路由到主库。
  • SQL 中包含FOR UPDATE等锁操作,强制路由到主库。

解决方案

  1. 确认schema.xmlbalance="1"balance="2"
  2. 避免在事务中混合读写,或使用READ COMMITTED隔离级别。
  3. 拆分读写事务,读操作单独执行。
7.2.2 数据一致性延迟

原因:主从异步复制存在延迟,写操作后立即读取可能读到旧数据。

解决方案

  1. 业务层缓存:写操作后更新缓存,读请求先查缓存。
  2. 强制读主:对实时性要求高的接口,强制路由到主库。
  3. 半同步复制:提升数据一致性,降低延迟窗口。
  4. 监控延迟:通过SHOW SLAVE STATUS\G中的Seconds_Behind_Master字段监控延迟,超过阈值时告警。

八、性能优化与高可用保障

8.1 主从复制性能优化

  1. 二进制日志优化

    • 使用binlog_format=ROW,减少日志体积,提升重放效率。
    • 配置expire_logs_days自动清理旧日志,避免磁盘占满。
    • 开启binlog_cache_size优化小事务日志写入。
  2. 从库并行复制:MySQL 5.7 + 支持并行复制,可提升从库重放速度:

    复制代码
    slave_parallel_type=LOGICAL_CLOCK
    slave_parallel_workers=4  # 并行线程数,建议与CPU核心数匹配
  3. 网络优化

    • 主从节点部署在同一机房,降低网络延迟。
    • 启用compress协议压缩日志传输:MASTER_COMPRESSION_ALGORITHM='zlib'

8.2 读写分离性能优化

  1. 负载均衡策略

    • 根据从库性能调整权重,避免性能差的从库过载。
    • 启用balance="2",读请求均匀分配到所有从库(包括主库)。
  2. 连接池优化

    • 配置 MyCat 连接池参数:maxConminCon,避免连接泄漏。
    • 优化 MySQL 连接数:max_connectionswait_timeout
  3. SQL 优化

    • 避免大表全表扫描,添加合适索引。
    • 拆分慢查询,分散读压力到多个从库。

8.3 高可用保障

  1. 主库故障转移

    • 引入 MHA(Master High Availability)工具,实现自动故障检测与主从切换。
    • 配置 VIP(虚拟 IP),切换后自动绑定到新主库,应用层无需修改配置。
  2. 从库高可用

    • 部署多个从库,避免单点故障。
    • MyCat 自动检测从库状态,故障从库自动剔除,恢复后重新加入。
  3. 监控告警

    • 使用 Prometheus+Grafana 监控主从延迟、连接数、QPS 等指标。
    • 配置告警规则,延迟超过阈值、节点故障时及时通知运维人员。

九、总结

本文从原理、实战、优化三个维度,全面解析了 MySQL 主从复制与读写分离技术:

  • 原理层面:深入讲解了主从复制的异步 / 半同步 / 组复制模式,以及读写分离的实现方式与核心挑战。
  • 实战层面:通过完整的步骤,演示了一主一从 MySQL 集群的搭建,以及基于 MyCat 的读写分离配置,验证了方案的可行性。
  • 优化层面:提供了主从复制与读写分离的性能优化策略,以及高可用保障方案,帮助读者构建稳定、高效的数据库架构。

在实际业务中,需根据业务规模、数据一致性要求、成本预算等因素,选择合适的复制模式与读写分离方案。对于中小型业务,一主一从 + MyCat 读写分离即可满足需求;对于大型核心业务,可考虑组复制 + 多从库 + MHA 高可用架构,进一步提升系统的可靠性与扩展性。

相关推荐
ego.iblacat2 小时前
MySQL 高可用
数据库·mysql·adb
bearpping2 小时前
关于Mysql 中 Row size too large (> 8126) 错误的解决和理解
数据库·mysql
Vic101013 小时前
Java深度分页性能优化:从问题本质到生产实践
java·adb·性能优化
爱丽_3 小时前
事务隔离级别与一致性:从现象到实现(MVCC 与当前读)
数据库·mysql
TRACER~853 小时前
项目实战:pandas+pytest+allure+adb
adb·pandas·pytest
X-⃢_⃢-X3 小时前
四、索引的创建与设计原则
数据库·mysql
满天星83035774 小时前
【MySQL】表的基本查询(上)
linux·服务器·数据库·mysql
川石课堂软件测试4 小时前
涨薪技术|Prometheus使用Recoding Rules优化性能
功能测试·测试工具·jmeter·mysql·面试·单元测试·prometheus
主角1 74 小时前
MySQL高可用集群
数据库·mysql