MySQL 二轮学习笔记·运维篇·(四) 读写分离
读写分离
1.简介
定义:读写分离的本质
将数据库的两种核心操作 “拆分” 到不同节点:
- 写操作(INSERT/UPDATE/DELETE):统一交给 “主库(Master)” 处理,保证数据写入的一致性;
- 读操作(SELECT):交给 “从库(Slave)” 处理,从库通过 “数据同步” 复制主库的内容,确保读数据的准确性。
简单理解:主库负责 “改数据”,从库负责 “查数据”,就像餐厅里 “厨师(主库)只负责做菜,服务员(从库)负责传菜”,分工明确效率更高。
为什么必须做读写分离?
单数据库的瓶颈很明显:
- 单库的 CPU、内存、IO 资源有限,大量读请求会占满 IO,导致写请求排队(比如用户下单 “写订单” 时,因大量 “查商品” 请求阻塞,出现 “下单失败”);
- 一旦单库宕机,整个业务直接瘫痪,可用性极低。
而读写分离能解决这些问题:
- 提升性能:读操作分散到多个从库,主库只处理写操作,资源不抢占,响应速度更快;
- 提高可用性:即使一个从库宕机,其他从库仍能提供读服务,不会影响业务;
- 扩展灵活:后续可通过增加从库数量,进一步分担读压力(比如从 1 个从库扩展到 3 个)。
2.基础架构:一主一从的搭建与实现
“一主一从” 是读写分离的最小可用架构——1 个主库 + 1 个从库,适合中小型 “读多写少” 业务(如初创公司的 APP、小型电商网站),配置简单、成本低,是入门读写分离的首选。
在搭建主从同步前,必须先完成 “环境一致性” 配置,否则后续同步会直接失败。以 MySQL 为例,我们需要 2 台服务器(或 2 个 MySQL 实例),假设配置如下:
节点角色 | IP 地址 | 核心配置要求 |
---|---|---|
主库(Master) | 192.168.1.10 | 开启 binlog 日志、指定唯一 server-id |
从库(Slave) | 192.168.1.11 | 指定与主库不同的唯一 server-id |
① 修改主库配置文件(my.cnf)
打开 /etc/my.cnf
,添加以下内容(开启 binlog 是主从同步的核心 —— 主库通过 binlog 记录写操作,从库读取 binlog 实现同步):
[mysqld]
server-id=10 # 唯一标识,不能与从库重复
log-bin=mysql-bin # 开启binlog,日志文件前缀为mysql-bin
binlog-do-db=test # 只同步test数据库(可选,不写则同步所有库)
修改后重启 MySQL:systemctl restart mysqld
。
② 主库创建同步账号
从库需要通过一个 “专用账号” 连接主库并读取 binlog,因此在主库执行 SQL:
-- 创建账号slave_user,允许从库IP(192.168.1.11)连接
CREATE USER 'slave_user'@'192.168.1.11' IDENTIFIED BY '123456';
-- 授予同步权限(REPLICATION SLAVE是核心权限)
GRANT REPLICATION SLAVE ON *.* TO 'slave_user'@'192.168.1.11';
-- 刷新权限
FLUSH PRIVILEGES;
③ 查看主库 binlog 信息
记录主库当前的 binlog 文件名和位置(后续从库同步需要用到):
SHOW MASTER STATUS;
执行后会得到类似结果:
File | Position | Binlog_Do_DB | Binlog_Ignore_DB |
---|---|---|---|
mysql-bin.000001 | 156 | test |
④ 修改从库配置文件(my.cnf)
打开 /etc/my.cnf
,添加:
[mysqld]
server-id=11 # 必须与主库不同
relay-log=mysql-relay-bin # 开启中继日志(从库读取主库binlog后,先存到中继日志,再执行)
重启从库 MySQL:systemctl restart mysqld
。
环境准备完成后,我们需要实现 “主从数据同步”,再配置 “读写路由”—— 让写操作走主库,读操作走从库。
第一步:实现主从同步
在从库执行以下 SQL,连接主库并开启同步:
-- 停止现有同步(首次配置可忽略)
STOP SLAVE;
-- 连接主库:替换IP、账号、密码、binlog文件名和位置
CHANGE MASTER TO
MASTER_HOST='192.168.1.10', # 主库IP
MASTER_USER='slave_user', # 同步账号
MASTER_PASSWORD='123456', # 密码
MASTER_LOG_FILE='mysql-bin.000001', # 主库的binlog文件名(上文记录的File)
MASTER_LOG_POS=156; # 主库的binlog位置(上文记录的Position)
-- 开启同步
START SLAVE;
第二步:验证同步是否成功
在从库执行以下 SQL,查看同步状态:
SHOW SLAVE STATUS\G; # \G表示按行显示,更清晰
核心看两个参数,必须都是 Yes:
Slave_IO_Running: Yes
(从库 IO 线程正常,能读取主库 binlog);Slave_SQL_Running: Yes
(从库 SQL 线程正常,能执行中继日志的 SQL)。
如果是 No,常见原因:
- 主从网络不通(检查防火墙,开放 3306 端口);
- 同步账号密码错误;
- binlog 文件名或位置填错。
第三步:实现读写分离
主从同步成功后,数据已经能从主库同步到从库,接下来要让 “写走主库,读走从库”,有两种常见方案:
方案 | 实现逻辑 | 优点 | 缺点 | 适用场景 |
---|---|---|---|---|
应用层拆分 | 在代码中判断 SQL 类型:- 写操作(INSERT/UPDATE/DELETE)→ 连接主库;- 读操作(SELECT)→ 连接从库 | 无需额外组件,成本低 | 代码耦合度高,后续修改麻烦(比如增加从库需改代码) | 小型项目、快速验证场景 |
中间件拆分 | 部署中间件(如 MyCat、Sharding-JDBC),应用只连接中间件,由中间件自动路由 | 解耦(应用无需关心主从),易扩展 | 需要额外维护中间件 | 中大型项目、生产环境 |
应用层拆分的例子:
// 主库数据源(写操作)
DataSource masterDataSource = createDataSource("192.168.1.10", "root", "123456");
// 从库数据源(读操作)
DataSource slaveDataSource = createDataSource("192.168.1.11", "root", "123456");
// 判断SQL类型,选择数据源
String sql = "SELECT * FROM user";
DataSource dataSource;
if (sql.startsWith("SELECT")) {
dataSource = slaveDataSource; // 读操作走从库
} else {
dataSource = masterDataSource; // 写操作走主库
}
3.进阶架构:双主双从的高可用实现
“一主一从” 有个致命问题:主库单点故障—— 如果主库宕机,所有写操作(如用户注册、下单)都会失败,业务直接中断。而 “双主双从” 通过 “两个主库互为主从、两个从库分担读压力”,解决了单点故障,是企业级生产环境的首选架构。
双主双从:架构原理
架构组成:2 个主库(Master1、Master2)+ 2 个从库(Slave1、Slave2),核心逻辑:
双主互备:Master1 和 Master2 互为主从 ——Master1 的写操作同步到 Master2,Master2 的写操作同步到 Master1,确保两个主库数据一致;
从库分担读压力:Slave1 同步 Master1 的数据,Slave2 同步 Master2 的数据,所有读操作路由到 Slave1 和 Slave2;
写操作负载均衡:两个主库都能处理写操作(需避免数据冲突),分摊写压力。
简单说:双主双从实现了 “写操作无单点、读操作可扩展”,即使一个主库宕机,另一个主库能立刻接管,业务不中断。
核心痛点:解决数据冲突
双主架构中,两个主库都能写数据,最容易出现 “自增 ID 冲突”—— 比如 Master1 插入一条数据 ID=1,Master2 也插入一条数据 ID=1,同步时会因 ID 重复报错。
解决方案:配置主库自增步长与起始值,让两个主库的自增 ID “不重叠”:
Master1:自增步长 = 2,起始值 = 1(生成 ID:1、3、5、7...);
Master2:自增步长 = 2,起始值 = 2(生成 ID:2、4、6、8...)。
配置方式(修改主库 my.cnf):
# Master1的my.cnf
auto-increment-increment=2 # 步长=2
auto-increment-offset=1 # 起始值=1
# Master2的my.cnf
auto-increment-increment=2 # 步长=2
auto-increment-offset=2 # 起始值=2
双主双从:搭建与读写分离
假设 4 个节点的 IP 和角色如下:
节点角色 | IP 地址 | server-id | 核心配置(my.cnf) |
---|---|---|---|
Master1 | 192.168.1.10 | 10 | 开启 binlog、自增步长 2 / 起始 1 |
Master2 | 192.168.1.12 | 12 | 开启 binlog、自增步长 2 / 起始 2 |
Slave1 | 192.168.1.11 | 11 | 开启中继日志 |
Slave2 | 192.168.1.13 | 13 | 开启中继日志 |
第一步:配置双主互同步
先让 Master1 和 Master2 互为主从:
- Master1→Master2 同步:在 Master2 执行 CHANGE MASTER TO,连接 Master1(步骤同 “一主一从”,需先在 Master1 创建同步账号);
- Master2→Master1 同步:在 Master1 执行 CHANGE MASTER TO,连接 Master2(需先在 Master2 创建同步账号);
- 分别在两个主库执行
START SLAVE
,并通过SHOW SLAVE STATUS\G
验证Slave_IO_Running
和Slave_SQL_Running
均为 Yes。
第二步:配置从库同步
让 Slave1 同步 Master1,Slave2 同步 Master2:
在 Slave1 执行 CHANGE MASTER TO,连接 Master1,开启同步;
在 Slave2 执行 CHANGE MASTER TO,连接 Master2,开启同步;
同样验证同步状态为 Yes。
第三步:实现读写分离与高可用切换
双主双从的读写分离必须依赖中间件(如 MyCat),核心实现两点:
读写路由:
写操作:轮询路由到 Master1 和 Master2(分摊写压力);
读操作:轮询路由到 Slave1 和 Slave2(分摊读压力)。
高可用切换:
手动切换:若 Master1 宕机,在中间件中禁用 Master1,写操作全部路由到 Master2;
自动切换(生产推荐):给双主配置Keepalived,设置一个 “虚拟 IP(VIP)”—— 应用通过 VIP 连接主库,当 Master1 宕机,Keepalived 自动将 VIP 漂移到 Master2,应用无需修改任何配置,实现 “无缝切换”。
本文系作者 @xiin 原创发布在To Future$站点。未经许可,禁止转载。
暂无评论数据