读写分离

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;

执行后会得到类似结果:

FilePositionBinlog_Do_DBBinlog_Ignore_DB
mysql-bin.000001156test

修改从库配置文件(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)
Master1192.168.1.1010开启 binlog、自增步长 2 / 起始 1
Master2192.168.1.1212开启 binlog、自增步长 2 / 起始 2
Slave1192.168.1.1111开启中继日志
Slave2192.168.1.1313开启中继日志

第一步:配置双主互同步

先让 Master1 和 Master2 互为主从:

  1. Master1→Master2 同步:在 Master2 执行 CHANGE MASTER TO,连接 Master1(步骤同 “一主一从”,需先在 Master1 创建同步账号);
  2. Master2→Master1 同步:在 Master1 执行 CHANGE MASTER TO,连接 Master2(需先在 Master2 创建同步账号);
  3. 分别在两个主库执行START SLAVE,并通过SHOW SLAVE STATUS\G验证Slave_IO_RunningSlave_SQL_Running均为 Yes。

第二步:配置从库同步

让 Slave1 同步 Master1,Slave2 同步 Master2:

在 Slave1 执行 CHANGE MASTER TO,连接 Master1,开启同步;

在 Slave2 执行 CHANGE MASTER TO,连接 Master2,开启同步;

同样验证同步状态为 Yes。

第三步:实现读写分离与高可用切换

双主双从的读写分离必须依赖中间件(如 MyCat),核心实现两点:

  1. 读写路由:

    写操作:轮询路由到 Master1 和 Master2(分摊写压力);

    读操作:轮询路由到 Slave1 和 Slave2(分摊读压力)。

  2. 高可用切换:

    手动切换:若 Master1 宕机,在中间件中禁用 Master1,写操作全部路由到 Master2;

    自动切换(生产推荐):给双主配置Keepalived,设置一个 “虚拟 IP(VIP)”—— 应用通过 VIP 连接主库,当 Master1 宕机,Keepalived 自动将 VIP 漂移到 Master2,应用无需修改任何配置,实现 “无缝切换”。

分类: Java-Backend 标签: MySQL

评论

暂无评论数据

暂无评论数据

目录