大数跨境
0
0

高可用数据库架构设计:读写分离的两大演进路径

高可用数据库架构设计:读写分离的两大演进路径 二进制跳动
2025-11-19
5
导读:高可用数据库架构设计:读写分离的两大演进路径

读写分离的基本原理是将数据库读写操作分散到不同的节点上。


一、读写分离架构

读写分离主要通过 主从架构(Master-Slave) 实现。

1. 基本原理

数据库服务器搭建一个主库与多个从库:

  • 主库(Master)负责写操作:包括 INSERT、UPDATE、DELETE。

  • 从库(Slave)负责读操作:SELECT。

  • 从库通过 复制机制(如 MySQL 的 binlog 复制)从主库同步数据。


主从数据流

  1. 主库将写操作写入 binlog。

  2. 从库通过 I/O 线程拉取 binlog。

  3. 从库 SQL 线程重放 binlog,将数据更新到从库。

-- 在主库开启 binlog[mysqld]server-id=1log-bin=mysql-bin
-- 在从库设置[mysqld]server-id=2relay-log=relay-binread_only=1
-- 从库执行CHANGE MASTER TO  MASTER_HOST='master_ip',  MASTER_USER='repl',  MASTER_PASSWORD='xxxx',  MASTER_LOG_FILE='mysql-bin.000001',  MASTER_LOG_POS=4;
START SLAVE;

2. 主从复制延迟问题与解决方式


延迟产生的原因

主库写入时并发高,从库处理 binlog 的速度不及主库写入速度,会导致延迟。例如主库写入成功,但从库还没同步,这时读请求从从库读到了旧数据。

解决方法

方法 1:写后读操作指定到主库

适用于对数据一致性要求高的场景。

  • 写操作后,紧跟着的一次读请求必须指定到主库。

  • 案例:用户下单后立即查看订单列表。

伪代码:

write_order(user_id, order_data)result = read_order_from_master(user_id)

方法 2:读从库失败后再读主库

适用于多数读不敏感 + 部分业务要强一致性的场景。

result = read_from_slave()if not result:    result = read_from_master()

方法 3:关键业务读写全部指向主库


非关键业务读写分离,关键业务不分离。

示例:

  • 支付、订单、库存 → 主库

  • 商品列表、推荐、评论 → 从库


3. 读写分离实现方式

读写分离的实际落地大致分为两类:

二、程序代码封装方式实现读写分离


由应用层自行决定 SQL 是读还是写,并路由到对应数据库。

1. 实现原理

应用代码中加入数据库访问层(DAO 层或网关),按照 SQL 类型或业务逻辑判断:

  • SELECT → 从库

  • INSERT / UPDATE / DELETE → 主库

也可在业务逻辑层标记某些操作必须读主库。

2. 典型方案

  • 早期淘宝的 TDDL

  • Spring 的 AbstractRoutingDataSource 扩展

  • 各公司自研的 DB Route 组件

3. 代码示例(Java)


public DataSource getDataSource(String sql) {    if (sql.trim().toLowerCase().startsWith("select")) {        return slaveDataSource;    } else {        return masterDataSource;    }}

或使用注解:

@ReadFromSlavepublic User getUser(long id) {    ...}


三、中间件封装实现读写分离


这是更先进、更稳定的方式。

1. 原理

通过一个数据库代理层,例如:

  • MySQL Router

  • Atlas(基于 MySQL Proxy)

  • ProxySQL
    应用程序只连接代理,代理根据 SQL 自动分发到主库或从库。

架构示例:

应用程序 → ProxySQL → Master/Slave

2. 特点

优点

  • 支持多种编程语言

  • 稳定性高

  • 故障转移自动化,可以探测主从状态

  • 对业务服务透明,无需修改代码

缺点

  • 实现复杂

  • 配置多

  • 需要专门运维


3. MySQL Router 示例

配置示例

[routing:read_write]bind_address = 0.0.0.0bind_port = 7001mode = read-writedestinations = master_ip:3306
[routing:read_only]bind_address = 0.0.0.0bind_port = 7002mode = read-onlydestinations = slave1_ip:3306, slave2_ip:3306

应用侧使用方式:

 
 
 

# 写请求连 7001mysql -h router -P7001
# 读请求连 7002mysql -h router -P7002

四、分库分表技术


当单库读写压力依然扛不住,或者数据量达到千亿级时,需要进行 分库分表

原理

通过分配规则(sharding)将数据分布到多个物理库或表中。


1. 数据分配机制

常见方式:

  • 哈希(user_id % 16)

  • 范围分片(按日期)

  • 地域分片(按城市)

示例:按照 user_id 分库分表

假设用户表非常大,拆成 4 个库,每个库 4 张表:

库名:

userdb_0userdb_1userdb_2userdb_3
表名:
user_0user_1user_2user_3
计算分布:
db_index = user_id % 4table_index = (user_id // 4) % 4

2. 实际案例:订单系统分库分表


订单号:order_id = 用户ID + 时间戳

规则:

  • 数据写进:orderdb_{order_id % 8}

  • 表路由:orders_{(order_id // 8) % 16}

好处:

  • 支持亿级订单数据

  • 查询性能提升 5~10 倍


五、读写分离 + 分库分表协同使用的实战架构

生产环境常见组合:

应用 → ProxySQL(读写分离) → 多主库 + 各自从库(分库分表) → 每库再水平扩展

优点:

  • 多维扩容

  • 支撑大规模业务(电商、直播、支付等)


六、总结

技术
场景适用性
优点
缺点
读写分离
读多写少
快速提升读性能
强一致性场景需特殊处理
分库分表
数据量大
解决性能瓶颈
复杂度高
程序封装
小团队可快速上手
定制强
难统一管理
中间件方式
中大型业务
高稳定、可扩展
需运维能力

【声明】内容源于网络
0
0
二进制跳动
15 年 + 技术老兵 架构师|技术总监|科技创业技术合伙人 曾任职苏宁科技、电讯盈科、联想云 专注架构设计与技术落地
内容 739
粉丝 0
二进制跳动 15 年 + 技术老兵 架构师|技术总监|科技创业技术合伙人 曾任职苏宁科技、电讯盈科、联想云 专注架构设计与技术落地
总阅读284
粉丝0
内容739