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

前言

在日常工作中,你可能遇到过这样的情况:公司业务越来越火,网站访问量暴涨,原本运行顺畅的数据库突然变得卡顿------查询数据要等好几秒,提交订单时甚至会超时。这不是数据库"偷懒",而是单台MySQL服务器扛不住了!

单台数据库就像一个独自干活的工人,既要处理用户的查询请求(比如查商品、查订单),又要处理数据修改操作(比如下单、改密码),高并发下很容易"忙不过来"。更麻烦的是,一旦这台服务器出故障,整个业务都会瘫痪,数据还可能丢失。

这时候,MySQL的主从复制读写分离技术就能派上大用场。简单说,主从复制能让多台数据库数据保持一致,读写分离能让"写操作"和"读操作"分开干活,不仅能提升速度,还能保证数据安全和服务稳定。接下来,我们就从原理到实战,一步步把这两个技术讲明白、做透彻。

一、MySQL主从复制原理

1.1 MySQL的复制类型

主从复制的核心是"复制数据变更",MySQL支持三种复制方式,各有侧重:

  • 基于语句的复制(STATEMENT):MySQL默认类型,简单说就是记录下主库执行过的SQL语句。比如主库执行了insert into user values(1,'张三'),从库就跟着执行同样的SQL。优点是日志文件小、效率高,缺点是有些特殊SQL(比如用了now()函数)可能复制不一致。
  • 基于行的复制(ROW):不记录SQL语句,而是直接记录每一行数据的变更细节。比如主库把"张三"改成了"李四",从库就直接定位到这一行,把值改成"李四"。优点是复制结果绝对一致,缺点是日志文件大、对大表操作效率稍低。
  • 混合类型的复制(MIXED):智能组合前两种方式。MySQL会自动判断SQL语句,如果是简单语句就用"基于语句"复制,如果是复杂语句(可能导致不一致的)就用"基于行"复制,兼顾效率和一致性。

1.2 MySQL主从复制的工作过程

主从复制靠"两日志、三线程"就能跑通,咱们用"快递配送"的逻辑来理解,就很容易懂:

  • 两日志:相当于快递的"发货单"和"中转单"
    1. 二进制日志(Binary Log):主库专属"发货单",主库每执行一次数据变更(增、删、改),都会先把这个操作记在二进制日志里,相当于发货前先填好发货单。
    2. 中继日志(Relay Log):从库专属"中转单",从库收到主库的"发货单"后,不会直接执行,而是先存到中继日志里,相当于快递到中转站后先登记,再安排派送。
  • 三线程:相当于快递的"发货员""中转员""派送员"
    1. Dump线程(主库):主库的"发货员",一旦发现二进制日志里有新的"发货单"(数据变更),就会把这些日志推送给从库。如果暂时没新操作,就会"待命"等待。
    2. I/O线程(从库):从库的"中转员",主动连接主库,接收Dump线程发来的"发货单"(二进制日志),然后原样写入从库的中继日志(中转单)。
    3. SQL线程(从库):从库的"派送员",不断读取中继日志里的"中转单",按照日志里的操作一步步执行,最终让从库数据和主库保持一致。

整个流程总结:主库写日志→Dump线程发日志→从库I/O线程收日志存中继→SQL线程执行日志→数据一致。

这里有个关键注意点:从库的SQL线程是"串行工作"的,也就是说主库同时执行10个更新操作,从库只能一个一个慢慢执行,这也是后面会提到"复制延迟"的原因之一。

1.2.1 主从复制延迟问题及解决

有时候会发现,主库修改了数据,过了几秒甚至几分钟从库才同步过来,这就是"复制延迟"。常见原因和解决方法如下:

  • 原因1:主库高并发,一下子产生大量事务(比如双十一期间,下单请求爆增),从库SQL线程忙不过来。
    解决:开启"并行复制",让从库能同时执行多个事务,相当于给派送员加了帮手。
  • 原因2:网络延迟,主库和从库不在一个机房,数据传输慢。
    解决:尽量让主从库在同一机房,优化网络带宽,避免跨地域同步。
  • 原因3:主从硬件差距大,主库用高性能服务器,从库用低配虚拟机,磁盘I/O、CPU处理速度跟不上。
    解决:给从库升级硬件,用物理机代替虚拟机,搭配SSD磁盘(读写速度比普通硬盘快10倍以上),增大从库内存(比如调整innodb_buffer_pool_size参数,让更多操作在内存中完成,减少磁盘读写)。
  • 原因4:默认是异步复制,主库不用等从库同步就直接返回结果,自然会有延迟。
    解决:改用"半同步复制"或"增强半同步复制",让主库等从库确认收到日志后再返回,既保证一致性,又不会太影响性能。

1.3 MySQL同步方式

除了上面提到的异步和半同步,MySQL还有4种常见同步方式,各有适用场景:

  • 异步复制(Async Replication):默认方式,主库写完二进制日志就直接处理下一个请求,不管从库有没有收到。优点是速度快、性能好,缺点是主库宕机时,可能有部分数据没同步到从库,导致数据丢失。适合对数据一致性要求不高、追求高性能的场景(比如博客、新闻网站)。
  • 同步复制(Sync Replication):主库写完日志后,必须等所有从库都执行完该事务,才返回结果。优点是数据绝对一致,不会丢失,缺点是性能极差,主库要一直等从库响应,高并发下会卡死。适合对数据安全性要求极高、并发量小的场景(比如金融交易记录)。
  • 半同步复制(Semi-Sync Replication):折中方案,主库写完日志后,只要等至少一个从库确认"收到日志并写入中继日志",就返回结果。既保证了数据不会丢失(至少有一个从库有备份),又不会像同步复制那样慢。需要注意的是,MySQL 5.5版本后才支持,主从库都要安装半同步插件。如果超过10秒(默认超时时间)没收到从库响应,会自动切换回异步复制。
  • 增强半同步复制(Lossless Semi-Sync Replication):MySQL 5.7版本引入的优化版,把"等待从库确认"的时机提前到"主库提交事务之前"。避免了半同步复制中"主库已提交事务,但从库还没同步,此时主库宕机,从库数据缺失"的问题,是目前最推荐的同步方式,兼顾安全和性能。

1.4 MySQL主从复制的应用场景

主从复制不只是"备份数据",更是构建高性能、高可用架构的基础,常见应用场景:

  1. 数据备份:从库相当于主库的实时备份,主库数据丢失时,能从从库快速恢复,不用依赖手动备份文件(手动备份可能有延迟)。
  2. 负载均衡:把读操作分担到从库,主库专门处理写操作,比如电商网站的"商品查询"请求都走从库,"下单、支付"请求走主库,减轻单台服务器压力。
  3. 高可用:主库故障时,能快速切换到从库,让业务继续运行,避免服务中断(比如用MHA、MGR等工具实现自动切换)。
  4. 架构演进:单台数据库→主从复制→读写分离→高可用集群(MHA/MGR),这是企业业务增长过程中,数据库架构的常见升级路径。

二、主从复制实战配置

接下来是实战环节,我们用3台CentOS 7.9服务器,搭建"1主2从"的主从复制集群,步骤详细到每一条命令,跟着做就能成功。

2.1 环境准备

先明确服务器配置,提前做好这些准备:

服务器角色 IP地址 操作系统 MySQL版本 核心要求
Master(主库) 10.0.0.21 CentOS 7.9 5.7 开启二进制日志、唯一server_id
Slave1(从库) 10.0.0.22 CentOS 7.9 5.7 开启中继日志、唯一server_id
Slave2(从库) 10.0.0.23 CentOS 7.9 5.7 开启中继日志、唯一server_id

准备工作:

  1. 3台服务器都关闭防火墙和SELinux(避免端口被拦截):

    bash 复制代码
    # 关闭防火墙(永久关闭,重启生效)
    systemctl stop firewalld
    systemctl disable firewalld
    # 关闭SELinux(临时关闭,重启后需重新设置)
    setenforce 0
    # 永久关闭SELinux,编辑配置文件
    vim /etc/selinux/config
    # 把SELINUX=enforcing改成SELINUX=disabled,保存退出后重启服务器
  2. 3台服务器都安装MySQL 5.7(编译安装或yum安装均可,这里略去安装步骤,可参考MySQL官方文档或网上教程,确保安装成功后能正常启动)。

2.2 主从服务器时间同步

主从库的时间必须一致,否则会导致复制出错(比如日志时间戳不匹配)。我们让Master作为时间源,Slave定期同步Master的时间。

2.2.1 Master服务器配置
  1. 安装时间同步工具ntp和ntpdate:

    bash 复制代码
    yum install ntp ntpdate -y
  2. 同步阿里云公共时间(确保Master本身时间准确):

    bash 复制代码
    ntpdate ntp.aliyun.com
  3. 编辑ntp配置文件,让Master成为时间同步源:

    bash 复制代码
    vim /etc/ntp.conf

    在文件末尾添加以下内容:

    复制代码
    # 设置本机为时间同步源
    server 127.127.1.0
    # 设置时间层级为10(0级是最高级时间源,咱们不用设置为0级)
    fudge 127.127.1.0 stratum 10
  4. 启动ntp服务,并设置开机自启:

    bash 复制代码
    systemctl start ntpd
    systemctl enable ntpd
2.2.2 两台Slave服务器配置
  1. 同样安装ntp和ntpdate工具:

    bash 复制代码
    yum install ntp ntpdate -y
  2. 手动同步Master的时间(首次同步):

    bash 复制代码
    ntpdate 10.0.0.21  # Master的IP地址
  3. 设置定时任务,每10分钟同步一次Master时间(避免时间漂移):

    bash 复制代码
    crontab -e

    在打开的文件中添加以下内容(按ESC,输入:wq保存退出):

    复制代码
    */10 * * * * /usr/sbin/ntpdate 10.0.0.21
PS:如果是外网环境

如果主从库不在同一内网(比如云服务器),没法通过内网IP同步时间,直接让所有服务器同步阿里云公共时间即可:

bash 复制代码
# 主库和从库都执行以下命令,设置定时任务
crontab -e
*/10 * * * * /usr/sbin/ntpdate ntp.aliyun.com

2.3 MySQL安装(略)

3台服务器都需要安装MySQL 5.7,推荐使用编译安装(稳定性更高),具体步骤可参考MySQL官方文档或"MySQL数据库基础与安装管理指南",核心要求:安装完成后能正常启动,root用户能登录。

2.4 配置主从同步

2.4.1 Master配置(主库)
  1. 编辑MySQL配置文件my.cnf

    bash 复制代码
    vim /etc/my.cnf

    [mysqld]模块下添加以下内容(确保没有重复的server_id配置):

    复制代码
    # 开启二进制日志,日志文件名为master-bin(后续会生成master-bin.000001、master-bin.000002等)
    log_bin=master-bin
    # 允许从库同步主库的日志(确保从库能复制主库的所有变更)
    log_slave-updates=true
    # 主库唯一ID(必须是1-2^32-1之间的整数,不能和从库重复)
    server_id=1
    # 可选:设置最大允许数据包大小,避免大事务传输失败
    max_allowed_packet=16M
  2. 保存配置后,重启MySQL服务:

    bash 复制代码
    systemctl restart mysqld
  3. 登录MySQL,创建从库同步专用账号(给从库授权,让它能复制主库数据):

    bash 复制代码
    mysql -uroot -p  # 输入root密码登录

    执行以下SQL语句:

    sql 复制代码
    # 创建用户myslave,允许10.0.0网段的服务器(从库)登录,密码123456
    GRANT REPLICATION SLAVE ON *.* TO 'myslave'@'10.0.0.%' IDENTIFIED BY '123456';
    # 刷新权限,让配置生效
    FLUSH PRIVILEGES;
    # 查看主库状态,记录下日志文件名(File)和位置(Position),后续从库配置要用
    SHOW MASTER STATUS;

    执行结果会显示类似以下内容(重点记File=master-bin.000001Position=1603,每个人的Position可能不同,以实际输出为准):

    复制代码
    +------------------+----------+--------------+------------------+-------------------+
    | File             | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
    +------------------+----------+--------------+------------------+-------------------+
    | master-bin.000001|      1603 |              |                  |                   |
    +------------------+----------+--------------+------------------+-------------------+

    注意:执行完SHOW MASTER STATUS后,不要关闭MySQL窗口,也不要在主库执行任何数据变更操作(否则Position会变)。

2.4.2 Slave配置(从库,两台从库配置完全一致)
  1. 编辑MySQL配置文件my.cnf

    bash 复制代码
    vim /etc/my.cnf

    [mysqld]模块下添加以下内容(Slave1的server_id=22,Slave2的server_id=23,不能重复,也不能和主库相同):

    复制代码
    # 开启二进制日志(可选,从库也可以作为其他从库的主库,方便架构扩展)
    log_bin=master-bin
    # 从库唯一ID(Slave1设22,Slave2设23)
    server_id=22
    # 开启中继日志,日志文件名为relay-log-bin
    relay-log=relay-log-bin
    # 中继日志索引文件(记录中继日志的列表)
    relay-log-index=slave-relay-bin.index
    # 可选:最大允许数据包大小
    max_allowed_packet=16M
  2. 保存配置后,重启MySQL服务:

    bash 复制代码
    systemctl restart mysqld
  3. 登录MySQL,配置主从同步参数(告诉从库主库的IP、账号、日志文件等信息):

    bash 复制代码
    mysql -uroot -p  # 输入root密码登录

    执行以下SQL语句(替换括号里的参数,注意和主库的SHOW MASTER STATUS结果一致):

    sql 复制代码
    CHANGE MASTER TO
    MASTER_HOST='10.0.0.21',  # 主库IP地址
    MASTER_USER='myslave',          # 主库创建的同步账号
    MASTER_PASSWORD='123456',       # 同步账号密码
    MASTER_LOG_FILE='master-bin.000001',  # 主库日志文件名(从SHOW MASTER STATUS获取)
    MASTER_LOG_POS=1603;  # 主库日志位置(从SHOW MASTER STATUS获取,每个人的可能不同)
  4. 启动从库同步进程:

    sql 复制代码
    START SLAVE;
  5. 查看从库同步状态,验证是否配置成功:

    sql 复制代码
    SHOW SLAVE STATUS\G;  # \G表示按行显示,更易读

    重点查看以下两个参数,只要都是Yes,就说明同步正常:

    复制代码
    Slave_IO_Running: Yes  # I/O线程正常运行(能收到主库日志)
    Slave_SQL_Running: Yes # SQL线程正常运行(能执行中继日志)

    如果是NoConnecting,说明有问题,可查看Last_IO_ErrorLast_SQL_Error字段,根据错误信息排查(比如密码错误、IP不对、日志文件名/位置填错)。

2.5 测试数据同步

配置完成后,我们用简单的操作测试一下,看看主库的数据变更是否能同步到从库:

  1. 登录主库MySQL,创建一个测试数据库:

    sql 复制代码
    CREATE DATABASE work;  # 创建数据库work
    SHOW DATABASES;  # 查看数据库,确认work已创建
  2. 分别登录两台从库MySQL,查看数据库列表:

    sql 复制代码
    SHOW DATABASES;

    如果能看到work数据库,说明主从复制成功!后续在主库执行insertupdatedelete操作,从库都会自动同步。

三、MySQL读写分离

主从复制搭建好后,就可以实现读写分离了。下面我们来看看读写分离的核心逻辑和实现方式。

3.1 什么是读写分离?

读写分离的核心思想很简单:把"写操作"和"读操作"分开,交给不同的数据库处理

  • 写操作(INSERT、UPDATE、DELETE):只能在主库执行,因为写操作会改变数据,必须让主库先记录,再同步到从库,避免多台服务器数据不一致。
  • 读操作(SELECT):可以在所有从库执行,比如用户查询商品、查看订单、浏览新闻等,都让从库来处理,主库只专注于处理写请求。

简单说,主库是"管理员",负责处理数据变更;从库是"客服",负责回应查询请求。这样一来,主库的压力减轻了,从库还能分担读压力,整体性能会大幅提升。

3.2 为什么需要读写分离?

前面我们提到过,单台数据库的瓶颈主要在"写操作"------写操作要修改数据、记录日志,耗时比读操作长得多(比如写10000条数据要3分钟,读10000条数据只要5秒)。

在实际业务中,读操作的频率通常是写操作的10倍以上(比如电商网站,用户逛商品、查物流的次数,远多于下单、付款的次数)。如果所有请求都走单台数据库,写操作会阻塞读操作,导致查询变慢,用户体验变差。

通过读写分离,能实现:

  • 减轻主库压力:主库不用处理大量读请求,专注于写操作,响应更快。
  • 提高查询效率:多台从库同时处理读请求,相当于多了多个"客服",并行处理能力更强。
  • 提高可用性:即使某一台从库故障,其他从库还能继续提供读服务,不会影响用户查询。

3.3 读写分离的实现方式

读写分离主要有两种实现方式,可根据实际情况选择:

3.3.1 基于程序代码实现

这是最常用的方式,在应用程序代码中添加"路由逻辑",判断请求是读还是写,然后转发到对应的数据库。

  • 实现逻辑:比如用Java开发,在MyBatis框架中配置两个数据源------主库数据源(处理写操作)和从库数据源(处理读操作)。执行insertupdatedelete时,用主库数据源;执行select时,用从库数据源。
  • 优点:性能好,没有中间环节,直接连接数据库,延迟低;灵活性高,可根据业务需求自定义路由规则(比如某些重要查询也走主库)。
  • 缺点:需要开发人员编写代码实现,对开发能力有要求;如果应用程序是多语言开发(比如Java、Python、PHP),每个语言都要单独实现,维护成本高。
  • 适用场景:单一语言开发的应用、对性能要求高、业务逻辑相对简单的场景(比如大部分互联网公司的后端服务)。
3.3.2 基于中间件代理实现

这种方式不需要修改应用程序代码,在应用程序和数据库之间加一个"中间件",由中间件来判断请求类型,转发到对应的数据库。应用程序只需要连接中间件,不用关心背后有多少主库和从库。

常见的中间件有以下几种,各有特点:

  • Amoeba(变形虫):
    优点:配置简单、易用性强,不用写复杂脚本,适合快速搭建;支持负载均衡(能把读请求均匀分配给多个从库)。
    缺点:不支持事务和存储过程,适合简单的CRUD业务,复杂业务(比如转账、订单提交)不适用。
  • Atlas:
    优点:基于MySQL-Proxy优化,支持事务和存储过程,性能稳定;360公司内部一直在用,每天能承载几十亿条读写请求,成熟可靠。
    缺点:配置相对复杂,需要一定的学习成本。
  • MyCat:
    优点:功能全面,支持读写分离、分库分表、高可用,社区活跃,文档丰富;适合大型复杂业务(比如电商、金融)。
    缺点:配置较复杂,对运维人员要求高,需要熟悉MyCat的核心概念(比如逻辑库、数据节点)。
  • MySQL-Proxy:
    优点:MySQL官方推出,灵活性高,支持自定义Lua脚本实现复杂路由规则。
    缺点:需要手动编写Lua脚本,开发成本高,稳定性不如Atlas和MyCat,企业中用得较少。

四、总结

MySQL主从复制和读写分离是企业级数据库架构的"基石"技术,核心价值在于提升性能、保证高可用、保障数据安全

简单回顾一下:

  • 主从复制是基础:通过"两日志、三线程"机制,让从库同步主库数据,解决了数据备份和负载均衡的基础问题。
  • 读写分离是延伸:在主从复制的基础上,把读写请求分开处理,进一步提升并发处理能力,解决了单台数据库的性能瓶颈。

在实际应用中,这两个技术通常一起使用,架构演进路径一般是:单台数据库→1主2从复制→读写分离→高可用集群(MHA/MGR)。

最后给新手几个建议:

  1. 搭建时先从简单的主从复制开始,测试通了再尝试读写分离,一步一步来。
  2. 配置过程中,注意server_id唯一、时间同步、日志文件名/位置正确,这些是最容易出错的地方。
  3. 生产环境中,一定要开启半同步/增强半同步复制,避免数据丢失;同时给从库配置高性能硬件,减少复制延迟。
  4. 遇到问题时,善用SHOW SLAVE STATUS\G查看错误信息,大部分问题都能通过错误日志排查解决。
相关推荐
爬山算法1 小时前
Redis(161)如何使用Redis实现分布式锁?
数据库·redis·分布式
可可苏饼干1 小时前
NoSQL 与 Redis
数据库·redis·笔记·学习·nosql
不穿格子的程序员1 小时前
MySQL篇3——MySQL深度揭秘:MySQL 索引失效情况与日志机制(redolog undolog binlog)介绍
数据库·mysql·索引失效·日志机制
麦芽糖02191 小时前
若依管理系统去掉Redis相关配置
数据库·redis·缓存
数据库学啊1 小时前
国产时序数据库厂家哪家好
数据库·时序数据库
数据库学啊1 小时前
好用的国产时序数据库哪个专业
数据库·时序数据库
数据库学啊1 小时前
口碑好的国产时序数据库企业
数据库·时序数据库
noravinsc1 小时前
如何清理全部已安装的mysql,之后重新安装mysql 8
数据库·mysql
用户6279947182622 小时前
南大通用GBase 8a堆栈收集方式汇总
数据库