MySQL主从复制与读写分离
文章目录
前言
本文围绕这一核心架构,从原理拆解到实操落地展开:先剖析主从复制的 "两日志、三线程" 核心机制与同步方式,再详解基于 CentOS 环境的 1 主 2 从部署实验;继而延伸至读写分离,对比不同实现方案,并通过 Amoeba 代理完成实操验证,同时覆盖常见故障排查方法,旨在帮助读者不仅理解 "是什么",更能掌握 "怎么做",为企业级 MySQL 架构落地提供可复用的技术指南。
一、主从复制
(一)核心原理
主从复制是实现数据多节点同步、为读写分离打基础的核心机制,其核心可总结为两日志、三线程,同时支持多种同步方式。
-
复制类型
- 基于语句的复制(STATEMENT,默认):直接记录执行的SQL语句,日志量小,但对函数、触发器等场景可能存在同步不一致。
- 基于行的复制(ROW):记录数据行的变更(如某行数据从A改成B),同步精度高,适合复杂业务,但日志量较大。
- 混合类型的复制(MIXED):自动切换两种模式,简单操作用STATEMENT,复杂操作切换为ROW,兼顾效率与一致性。
-
核心组件:两日志+三线程
- 两日志
- 二进制日志(Binary log):主库特有,记录所有修改数据的SQL操作(不记录SELECT等读操作),是主从同步的数据源。
- 中继日志(Relay log):从库特有,从主库同步二进制日志后,会先写入中继日志,再由SQL线程重放执行。
- 三线程
- dump线程:主库端线程,负责响应从库I/O线程的请求,将主库二进制日志的事件发送给从库。
- I/O线程:从库端线程,负责连接主库、拉取二进制日志事件,并写入本地中继日志。
- SQL线程:从库端线程,负责读取中继日志中的事件,重放SQL操作以同步主库数据。
- 两日志
-
同步方式
同步方式 核心逻辑 优缺点 异步复制(默认) 主库写入二进制日志后直接响应客户端,不等待从库同步 性能最优,但主库宕机可能丢失未同步数据 同步复制 主库需等待所有从库完成数据同步并执行成功后,才响应客户端 数据安全性最高,但性能损耗极大,几乎不用于生产 半同步复制(MySQL5.5+) 主库等待至少1个从库将日志写入中继日志后,再响应客户端 兼顾性能与安全性,超时会自动切回异步 增强半同步复制(MySQL5.7+,无损复制) 将等待从库ACK的时机放在主库事务提交前,避免主从切换的数据不一致 生产常用,是半同步的优化版本 -
主从复制延迟及解决
- 延迟原因:主库高并发事务堆积、网络延迟、主从硬件性能差异、异步复制的机制特性。
- 优化方案 :从库加大内存缓存(如调大
innodb_buffer_pool_size)、使用高性能物理机和SSD磁盘、优化网络(避免跨机房同步);也可开启并行复制 解决从库串行执行的瓶颈,开启半同步复制解决数据丢失问题。
(二)主从复制实验(详细部署与操作)
本实验基于CentOS7.9环境,搭建1主(192.168.10.16)2从(192.168.10.14/15,均为MySQL5.7)的同步架构。
-
前置步骤:主从服务器时间同步
时间不一致会导致日志同步时序混乱,因此需先完成时间校准。
-
Master服务器配置
-
安装NTP工具:
yum -y install ntpdate ntp -
同步阿里云公共时间源:
ntpdate ntp.aliyun.com -
修改NTP配置文件(
vi /etc/ntp.conf),添加本机为内网时间源:conffudge 127.127.1.0 stratum 10 # 本机时间层级(非0级,不对外提供公共时间) server 127.127.1.0 # 本机作为内网时间源 -
启动NTP服务并关闭防火墙/SELinux:
bashsystemctl start ntpd systemctl stop firewalld setenforce 0
-
-
Slave服务器配置
- 安装NTP工具:
yum install ntp ntpdate -y - 关闭防火墙/SELinux,同步Master的时间:
ntpdate 192.168.10.16 - 配置定时任务,每10分钟同步一次:
crontab -e,添加*/10 * * * * /usr/sbin/ntpdate 192.168.10.16
- 安装NTP工具:
-
-
核心步骤:配置主从同步
- Master服务器配置
-
修改MySQL配置文件(
vi /etc/my.cnf),在mysqld模块添加如下内容,开启二进制日志并设置唯一ID:conflog_bin=master-bin # 二进制日志文件名前缀 log_slave-updates=true # 允许从库同步日志 server_id = 1 # 主库ID,需唯一 -
重启MySQL服务:
systemctl restart mysqld -
登录MySQL,创建同步专用账号并授权(允许192.168.10.0网段的从库连接):
sqlGRANT REPLICATION SLAVE ON *.* TO 'myslave'@'192.168.10.%' IDENTIFIED BY '123456'; flush privileges; # 刷新权限 -
查看主库状态,记录二进制日志文件名和偏移量(后续从库需用到):
sqlshow master status;示例结果中,
File为master-bin.000001,Position为412,这两个值是从库同步的"起点"。
-
- Slave服务器配置(两台从库配置一致,以192.168.10.14为例)
-
修改MySQL配置文件(
vi /etc/my.cnf),开启中继日志并设置唯一ID:conflog-bin=master-bin server_id = 22 # 从库ID,不可与主库/其他从库重复(另一台从库设为23) relay-log=relay-log-bin # 中继日志文件名前缀 relay-log-index=slave-relay-bin.index # 中继日志索引文件 -
重启MySQL服务,登录MySQL后配置主库连接信息,指定同步起点:
sqlchange master to master_host='192.168.10.16', # 主库IP master_user='myslave', # 同步专用账号 master_password='123456', # 账号密码 master_log_file='master-bin.000001', # 主库二进制日志文件名 master_log_pos=412; # 主库日志偏移量 -
启动从库同步功能并查看状态:
sqlstart slave; show slave status\G; # 注意结尾的\G用于格式化输出关键状态指标:
Slave_IO_Running=Yes且Slave_SQL_Running=Yes,表示从库I/O线程和SQL线程均正常运行,同步链路已打通。
-
- Master服务器配置
-
测试步骤:验证数据同步
这里选取创建数据库的示例进行验证:
- 在Master服务器登录MySQL,执行
create database work;创建名为work的数据库; - 分别在两台Slave服务器登录MySQL,执行
show databases;,可看到work数据库已自动同步,证明主从复制生效。
- 在Master服务器登录MySQL,执行
(三)主从同步故障排查(常见问题)
- 主从不一致 :可在从库执行
stop slave; SET GLOBAL SQL_SLAVE_SKIP_COUNTER=1; START SLAVE;跳过当前错误继续同步(生产需谨慎,需先定位错误根源)。 - I/O线程一直Connecting :大概率是二进制日志位置不匹配,可在主库执行
flush logs;生成新日志,再重新获取主库日志文件名和偏移量,在从库执行change master to指定新的同步起点。
二、读写分离
(一)核心原理
-
定义 :让主库处理增删改等写操作 (INSERT/UPDATE/DELETE),从库处理查询等读操作(SELECT),通过主从复制同步写操作产生的数据变更,从而分担数据库压力。
-
实现方式
实现方式 核心逻辑 优缺点 程序代码内部实现 在代码中根据SQL类型路由(SELECT走从库,写操作走主库) 性能优、无额外硬件成本;但需开发人员实现,对大型应用代码改动大 中间代理层实现 代理服务器接收客户端请求,自动分发到对应数据库 无需改动业务代码;需部署代理服务,有一定性能损耗(常用工具:Amoeba/Atlas/MySQL-Proxy)
(二)读写分离实验(基于Amoeba代理,详细部署)
本实验基于CentOS7.6环境,在已搭建1主2从的基础上,增加Amoeba代理服务器(192.168.10.80,需JDK1.6)和客户端服务器(192.168.10.13)实现读写分离。
-
前置要求:已完成1主2从的主从复制搭建,确保主从数据同步正常。
-
Amoeba服务器配置
Amoeba基于Java开发,需先配置Java环境,再安装和配置Amoeba。
- 步骤1:安装JDK1.6环境
-
将JDK安装包上传到
/opt目录,移动到/usr/local并赋予执行权限:bashcd /opt cp jdk-6u14-linux-x64.bin /usr/local/ cd /usr/local chmod +x jdk-6u14-linux-x64.bin ./jdk-6u14-linux-x64.bin # 按提示输入yes并回车完成安装 -
重命名JDK目录并配置环境变量(
vi /etc/profile):bashmv jdk1.6.0_14/ /usr/local/jdk1.6 vi /etc/profile添加如下环境变量:
confexport JAVA_HOME=/usr/local/jdk1.6 export CLASSPATH=$CLASSPATH:$JAVA_HOME/lib:$JAVA_HOME/jre/lib export PATH=$JAVA_HOME/lib:$JAVA_HOME/jre/bin/:$PATH:$HOME/bin export AMOEBA_HOME=/usr/local/amoeba export PATH=$PATH:$AMOEBA_HOME/bin -
生效环境变量并验证:
source /etc/profile; java -version,能显示JDK1.6版本即配置成功。
-
- 步骤2:安装Amoeba软件
-
创建安装目录并解压安装包:
bashmkdir /usr/local/amoeba tar zxvf amoeba-mysql-binary-2.2.0.tar.gz -C /usr/local/amoeba/ chmod -R 755 /usr/local/amoeba/ -
验证安装:执行
/usr/local/amoeba/bin/amoeba,输出amoeba start|stop即安装成功。
-
- 步骤3:配置Amoeba读写分离
- 先在主从库授权Amoeba访问:在Master、Slave1、Slave2的MySQL中执行
grant all on *.* to test@'192.168.10.%' identified by '123456';,允许Amoeba服务器用test账号访问。 - 修改Amoeba主配置文件(
vi /usr/local/amoeba/conf/amoeba.xml):- 30行:修改客户端连接Amoeba的账号为
amoeba; - 32行:修改客户端连接密码为
123456; - 115行:设置默认连接池为
master; - 117行:取消注释,指定
writePool=master(写操作走主库)、readPool=slaves(读操作走从库池)。
- 30行:修改客户端连接Amoeba的账号为
- 修改数据库服务器配置文件(
vi /usr/local/amoeba/conf/dbServers.xml):- 23行:注释掉
<property name="schema">test</property>(避免无test库时报错); - 26行:修改访问数据库的账号为
test; - 28行:取消注释,设置数据库访问密码为
123456; - 45行:设置主库节点名为
master,48行填写主库IP192.168.10.16; - 52行:设置从库1节点名为
slave1,55行填写IP192.168.10.14; - 58行:复制从库1配置,新增从库2节点
slave2,IP为192.168.10.15; - 65行:设置从库池
slaves为虚拟节点,71行指定池内节点为slave1,slave2(实现读负载均衡)。
- 23行:注释掉
- 启动Amoeba:
/usr/local/amoeba/bin/amoeba start&,用netstat -anpt | grep java查看8066端口(Amoeba默认端口)是否开启。
- 先在主从库授权Amoeba访问:在Master、Slave1、Slave2的MySQL中执行
- 步骤1:安装JDK1.6环境
-
测试步骤:验证读写分离
在客户端服务器安装MariaDB,执行
mysql -u amoeba -p123456 -h 192.168.10.80 -P8066通过Amoeba访问数据库,进行如下验证:- 主库创建测试表:在Master的MySQL中执行
use db_test; create table test (id int(10),name varchar(10),address varchar(20));; - 从库关闭同步并插入专属数据:Slave1执行
stop slave; insert into test values('1','zhangsan','this_is_slave1');,Slave2执行stop slave; insert into test values('2','lisi','this_is_slave2');;主库执行insert into test values('3','wangwu','this_is_master');; - 客户端执行
select * from test;,只会读取到Slave1/Slave2的专属数据(读操作走从库);执行insert into test values('4','qianqi','this_is_client');,仅主库会新增该数据(写操作走主库); - 从库重启同步后(
start slave;),客户端插入的数据会同步到从库,实现数据最终一致。
- 主库创建测试表:在Master的MySQL中执行
三、核心总结
- 主从复制是读写分离的基础,核心依赖二进制日志+中继日志 和dump/I/O/SQL线程 ,生产常用增强半同步复制保障数据安全;
- 读写分离通过代理层(如Amoeba)实现读写请求分流,有效提升数据库并发处理能力;
- 实验中需重点关注时间同步、主从配置参数、代理层权限与节点映射,故障排查优先检查日志和线程状态。
总结
MySQL 主从复制与读写分离,是企业从 "单库架构" 迈向 "高可用、高并发架构" 的关键一步,二者相辅相成、缺一不可 ------ 主从复制是基础,通过二进制日志与中继日志的联动、dump/I/O/SQL 线程的协作,实现了数据跨节点同步,既解决了单点故障风险,也为读写分离提供了数据一致性保障;读写分离是延伸,通过 "主库写、从库读" 的请求分流,有效化解了 "写操作耗时拖累读性能" 的矛盾,显著提升数据库并发承载能力。