前言
在日常工作中,你可能遇到过这样的情况:公司业务越来越火,网站访问量暴涨,原本运行顺畅的数据库突然变得卡顿------查询数据要等好几秒,提交订单时甚至会超时。这不是数据库"偷懒",而是单台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主从复制的工作过程
主从复制靠"两日志、三线程"就能跑通,咱们用"快递配送"的逻辑来理解,就很容易懂:
- 两日志:相当于快递的"发货单"和"中转单"
- 二进制日志(Binary Log):主库专属"发货单",主库每执行一次数据变更(增、删、改),都会先把这个操作记在二进制日志里,相当于发货前先填好发货单。
- 中继日志(Relay Log):从库专属"中转单",从库收到主库的"发货单"后,不会直接执行,而是先存到中继日志里,相当于快递到中转站后先登记,再安排派送。
- 三线程:相当于快递的"发货员""中转员""派送员"
- Dump线程(主库):主库的"发货员",一旦发现二进制日志里有新的"发货单"(数据变更),就会把这些日志推送给从库。如果暂时没新操作,就会"待命"等待。
- I/O线程(从库):从库的"中转员",主动连接主库,接收Dump线程发来的"发货单"(二进制日志),然后原样写入从库的中继日志(中转单)。
- 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主从复制的应用场景
主从复制不只是"备份数据",更是构建高性能、高可用架构的基础,常见应用场景:
- 数据备份:从库相当于主库的实时备份,主库数据丢失时,能从从库快速恢复,不用依赖手动备份文件(手动备份可能有延迟)。
- 负载均衡:把读操作分担到从库,主库专门处理写操作,比如电商网站的"商品查询"请求都走从库,"下单、支付"请求走主库,减轻单台服务器压力。
- 高可用:主库故障时,能快速切换到从库,让业务继续运行,避免服务中断(比如用MHA、MGR等工具实现自动切换)。
- 架构演进:单台数据库→主从复制→读写分离→高可用集群(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 |
准备工作:
-
3台服务器都关闭防火墙和SELinux(避免端口被拦截):
bash# 关闭防火墙(永久关闭,重启生效) systemctl stop firewalld systemctl disable firewalld # 关闭SELinux(临时关闭,重启后需重新设置) setenforce 0 # 永久关闭SELinux,编辑配置文件 vim /etc/selinux/config # 把SELINUX=enforcing改成SELINUX=disabled,保存退出后重启服务器 -
3台服务器都安装MySQL 5.7(编译安装或yum安装均可,这里略去安装步骤,可参考MySQL官方文档或网上教程,确保安装成功后能正常启动)。
2.2 主从服务器时间同步
主从库的时间必须一致,否则会导致复制出错(比如日志时间戳不匹配)。我们让Master作为时间源,Slave定期同步Master的时间。
2.2.1 Master服务器配置
-
安装时间同步工具ntp和ntpdate:
bashyum install ntp ntpdate -y -
同步阿里云公共时间(确保Master本身时间准确):
bashntpdate ntp.aliyun.com -
编辑ntp配置文件,让Master成为时间同步源:
bashvim /etc/ntp.conf在文件末尾添加以下内容:
# 设置本机为时间同步源 server 127.127.1.0 # 设置时间层级为10(0级是最高级时间源,咱们不用设置为0级) fudge 127.127.1.0 stratum 10 -
启动ntp服务,并设置开机自启:
bashsystemctl start ntpd systemctl enable ntpd
2.2.2 两台Slave服务器配置
-
同样安装ntp和ntpdate工具:
bashyum install ntp ntpdate -y -
手动同步Master的时间(首次同步):
bashntpdate 10.0.0.21 # Master的IP地址 -
设置定时任务,每10分钟同步一次Master时间(避免时间漂移):
bashcrontab -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配置(主库)
-
编辑MySQL配置文件
my.cnf:bashvim /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 -
保存配置后,重启MySQL服务:
bashsystemctl restart mysqld -
登录MySQL,创建从库同步专用账号(给从库授权,让它能复制主库数据):
bashmysql -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.000001和Position=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配置(从库,两台从库配置完全一致)
-
编辑MySQL配置文件
my.cnf:bashvim /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 -
保存配置后,重启MySQL服务:
bashsystemctl restart mysqld -
登录MySQL,配置主从同步参数(告诉从库主库的IP、账号、日志文件等信息):
bashmysql -uroot -p # 输入root密码登录执行以下SQL语句(替换括号里的参数,注意和主库的
SHOW MASTER STATUS结果一致):sqlCHANGE 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获取,每个人的可能不同) -
启动从库同步进程:
sqlSTART SLAVE; -
查看从库同步状态,验证是否配置成功:
sqlSHOW SLAVE STATUS\G; # \G表示按行显示,更易读重点查看以下两个参数,只要都是
Yes,就说明同步正常:Slave_IO_Running: Yes # I/O线程正常运行(能收到主库日志) Slave_SQL_Running: Yes # SQL线程正常运行(能执行中继日志)如果是
No或Connecting,说明有问题,可查看Last_IO_Error或Last_SQL_Error字段,根据错误信息排查(比如密码错误、IP不对、日志文件名/位置填错)。
2.5 测试数据同步
配置完成后,我们用简单的操作测试一下,看看主库的数据变更是否能同步到从库:
-
登录主库MySQL,创建一个测试数据库:
sqlCREATE DATABASE work; # 创建数据库work SHOW DATABASES; # 查看数据库,确认work已创建 -
分别登录两台从库MySQL,查看数据库列表:
sqlSHOW DATABASES;如果能看到
work数据库,说明主从复制成功!后续在主库执行insert、update、delete操作,从库都会自动同步。
三、MySQL读写分离
主从复制搭建好后,就可以实现读写分离了。下面我们来看看读写分离的核心逻辑和实现方式。
3.1 什么是读写分离?
读写分离的核心思想很简单:把"写操作"和"读操作"分开,交给不同的数据库处理。
- 写操作(INSERT、UPDATE、DELETE):只能在主库执行,因为写操作会改变数据,必须让主库先记录,再同步到从库,避免多台服务器数据不一致。
- 读操作(SELECT):可以在所有从库执行,比如用户查询商品、查看订单、浏览新闻等,都让从库来处理,主库只专注于处理写请求。
简单说,主库是"管理员",负责处理数据变更;从库是"客服",负责回应查询请求。这样一来,主库的压力减轻了,从库还能分担读压力,整体性能会大幅提升。
3.2 为什么需要读写分离?
前面我们提到过,单台数据库的瓶颈主要在"写操作"------写操作要修改数据、记录日志,耗时比读操作长得多(比如写10000条数据要3分钟,读10000条数据只要5秒)。
在实际业务中,读操作的频率通常是写操作的10倍以上(比如电商网站,用户逛商品、查物流的次数,远多于下单、付款的次数)。如果所有请求都走单台数据库,写操作会阻塞读操作,导致查询变慢,用户体验变差。
通过读写分离,能实现:
- 减轻主库压力:主库不用处理大量读请求,专注于写操作,响应更快。
- 提高查询效率:多台从库同时处理读请求,相当于多了多个"客服",并行处理能力更强。
- 提高可用性:即使某一台从库故障,其他从库还能继续提供读服务,不会影响用户查询。
3.3 读写分离的实现方式
读写分离主要有两种实现方式,可根据实际情况选择:
3.3.1 基于程序代码实现
这是最常用的方式,在应用程序代码中添加"路由逻辑",判断请求是读还是写,然后转发到对应的数据库。
- 实现逻辑:比如用Java开发,在MyBatis框架中配置两个数据源------主库数据源(处理写操作)和从库数据源(处理读操作)。执行
insert、update、delete时,用主库数据源;执行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)。
最后给新手几个建议:
- 搭建时先从简单的主从复制开始,测试通了再尝试读写分离,一步一步来。
- 配置过程中,注意
server_id唯一、时间同步、日志文件名/位置正确,这些是最容易出错的地方。 - 生产环境中,一定要开启半同步/增强半同步复制,避免数据丢失;同时给从库配置高性能硬件,减少复制延迟。
- 遇到问题时,善用
SHOW SLAVE STATUS\G查看错误信息,大部分问题都能通过错误日志排查解决。