摘要
读写分离是提升数据库吞吐量和降低主库压力的经典架构模式。本文从概念原理出发,对比传统 MySQL 主从 + 中间件代理方案与 TiDB 原生智能路由方案的优劣差异,帮助架构师和 DBA 选择适合业务的读写分离策略。本文适合后端架构师、DBA、技术负责人及对数据库架构选型感兴趣的开发者阅读。
一、读写分离的概念与价值
1.1 什么是读写分离
读写分离(Read/Write Splitting)是指将数据库的写操作路由到主节点(Primary),将读操作分散到多个从节点(Replica)的架构模式。其核心目标是:
- 提升读吞吐:通过增加只读副本数量水平扩展读能力
- 降低主库压力:将查询请求分流,避免主库成为瓶颈
- 优化用户体验:读请求就近路由到最近副本,降低网络延迟
1.2 典型应用场景
| 场景 | 读/写比 | 读写分离收益 |
|---|---|---|
| 电商商品详情页 | 100:1 | 显著,读副本可承载绝大部分流量 |
| 社交信息流 | 50:1 | 高,关注列表/消息列表均为读密集 |
| ERP/CRM 系统 | 10:1 | 中等,报表查询可分流到从库 |
| 金融交易系统 | 3:1 | 有限,强一致性要求高,读延迟敏感 |
根据业内统计,大多数在线业务系统的读写比在 5:1 到 100:1 之间,读写分离在大多数场景下都能带来明显收益。
二、传统读写分离方案
2.1 架构概述
传统方案基于 MySQL 主从复制架构,通过中间件代理实现读写路由:
应用层
│
▼
代理层(ProxySQL / MyCat / ShardingSphere-Proxy)
├── 写请求 ──► MySQL Primary(主库)
└── 读请求 ──► MySQL Replica × N(从库)
▲
│ binlog 异步复制
└── MySQL Primary
2.2 主流中间件对比
| 中间件 | 优势 | 劣势 |
|---|---|---|
| ProxySQL | 轻量、高性能(C++ 实现)、查询缓存 | 配置复杂,社区版功能受限 |
| MyCat | 功能丰富,支持分库分表 | 性能开销较大,维护活跃度下降 |
| ShardingSphere-Proxy | Java 生态友好,Apache 基金会项目 | Java 运行时资源消耗相对较高 |
| MySQL Router | Oracle 官方出品,配置简单 | 功能较基础,适合简单场景 |
2.3 传统方案的核心问题
数据延迟:主从复制基于 binlog 异步同步,从库数据存在毫秒到秒级延迟。在写入后立即读取的场景中,可能出现读到旧数据的问题。
运维复杂度:需额外维护代理层,包括代理的高可用部署、配置变更、故障切换等。代理层本身成为新的故障点和运维负担。
连接管理:应用与数据库之间多了一跳网络,增加延迟的同时也增加了连接管理的复杂度。
-- 传统方案中处理主从延迟的常见做法
-- 方式 1:强制走主库
SET @read_from_primary = 1; -- 需代理层支持
-- 方式 2:等待延迟消除
SELECT MASTER_WAIT(1000); -- 等待最多 1 秒
三、TiDB 智能路由方案
3.1 架构原理
TiDB 采用计算-存储分离架构,天然具备读写分离能力,且无需额外中间件:
应用层
│
▼
TiDB Server(计算节点,无状态,可水平扩展)
│
▼
PD Server(元数据管理 + 调度)
│
├── TiKV Leader ──► 写请求
└── TiKV Follower ──► 读请求(智能路由)
3.2 Follower Read(跟随者读)
Follower Read 允许 TiDB 将一致性读请求路由到 Raft Follower 节点,大幅提升读吞吐能力:
-- 使用 Follower Read(需指定近似时间戳或 GC 安全点内的时间)
SELECT * FROM orders WHERE region = '华东'
AS OF TIMESTAMP '2026-01-15 10:00:00';
-- 开启自动 Follower Read(默认关闭)
SET SESSION tidb_read_staleness = '-10'; -- 读取 10 秒前的数据
核心特性:
| 特性 | 说明 |
|---|---|
| 无额外组件 | 无需代理层或中间件,TiDB 原生支持 |
| 一致性保证 | 基于 Raft 一致性协议,确保读到已提交数据 |
| 自动路由 | PD 调度器自动选择最优副本读取 |
| 弹性扩展 | 增加 TiKV 节点即可提升读吞吐,自动均衡 |
3.3 Stale Read(旧数据读)
Stale Read 是 TiDB 5.4+ 版本推出的更简便的读取模式:
-- 设置读时间窗口(相对于当前时间)
SET SESSION tidb_read_staleness = '-5'; -- 允许 5 秒内的旧数据
-- 之后的 SELECT 自动路由到 Follower
SELECT COUNT(*) FROM orders WHERE create_time > '2025-01-01';
Stale Read 与 Follower Read 的关键区别:
| 维度 | Follower Read | Stale Read |
|---|---|---|
| 配置方式 | SQL 级别指定时间戳 | Session 级别设置时间窗口 |
| 适用版本 | TiDB 3.0+ | TiDB 5.4+ |
| 使用便捷性 | 需要传入具体时间戳 | 设置一次,后续查询自动生效 |
| 典型场景 | 报表查询、批处理 | 实时性要求不高的业务查询 |
四、TiDB vs 传统方案对比
4.1 综合对比
| 维度 | 传统 MySQL + Proxy | TiDB 智能路由 |
|---|---|---|
| 架构复杂度 | 高(需维护代理 + 主从) | 低(原生内置,无需中间件) |
| 数据延迟 | 毫秒-秒级(异步复制) | 可控(基于 Raft,时间戳精确) |
| 一致性保证 | 最终一致性 | 强一致性(Raft 协议) |
| 运维成本 | 高(代理 HA、配置管理) | 低(自动调度,零配置) |
| 弹性扩展 | 需手动增加从库 | 增加 TiKV 节点,自动均衡 |
| 故障自愈 | 需人工或 MHA 自动切换 | Raft 自动选举,秒级恢复 |
| 读写切换逻辑 | 代理层硬编码 | 无需切换,统一 SQL 接口 |
4.2 性能对比参考
在 TiDB 官方测试中(TPC-C 1000 Warehouse 场景):
| 方案 | 读 QPS | 写 TPS | P99 读延迟 |
|---|---|---|---|
| MySQL 单主 | ~50,000 | ~10,000 | 3ms |
| MySQL + 3 从库 + ProxySQL | ~150,000 | ~10,000 | 5ms(含代理开销) |
| TiDB 3 TiDB + 9 TiKV | ~400,000 | ~10,000 | 2ms |
TiDB 通过多副本并行读取和自动路由,在保持写性能的同时将读吞吐提升 8 倍。
五、读写分离最佳实践
5.1 TiDB 读写分离实践建议
-- 1. 连接池配置:读写分离场景建议增大连接池
-- Spring Boot 配置示例
spring.datasource.hikari.maximum-pool-size=50
-- 2. 将报表/分析查询路由到 Follower
SET SESSION tidb_read_staleness = '-30';
-- 执行报表查询...
-- 3. 关键业务保持默认(Leader Read),确保最新数据
-- 无需额外配置,默认即走 Leader
5.2 不适合读写分离的场景
- 强一致性要求高的金融交易(写入后必须立即读到最新值)
- 实时库存扣减(超卖敏感场景)
- 实时消息推送(写入后必须立即可见)
这些场景下建议保持 Leader Read,TiDB 的分布式架构本身已能提供足够高的吞吐量。
5.3 从传统方案迁移到 TiDB 的建议
- 评估读写比:分析业务实际读写比,确定读写分离的实际需求
- 灰度切换:先将低风险业务的读流量切换到 Follower Read,验证效果
- 逐步下线代理:确认 Follower Read 稳定后,移除 ProxySQL 等中间件
- 监控对比:在切换前后对比延迟、吞吐、错误率等指标
六、总结
读写分离是提升数据库读吞吐量的有效手段。传统方案通过中间件代理 + 主从复制实现,但带来了运维复杂度和数据延迟问题。TiDB 基于计算-存储分离和 Raft 一致性协议,提供了无中间件、强一致性的原生读写分离方案,大幅降低了架构复杂度和运维成本。对于读写比高、读性能有要求的业务,TiDB 的 Follower Read 和 Stale Read 是更现代、更简洁的选择。
下一步行动
- 试用 TiDB Follower Read:TiDB Cloud 免费试用 — 免费创建 TiDB Serverless 集群,体验原生读写分离
- 下载 POC 测试环境:TiUP 部署文档 — 使用 TiUP 快速部署测试集群,验证读写分离效果
- 阅读 TiDB 读写分离技术文档:Follower Read 文档 — 了解 Follower Read 的完整技术细节
常见问题(FAQ)
Q1:TiDB Follower Read 会有数据延迟吗?
Follower Read 的延迟取决于 Raft 复制延迟,通常在毫秒级。通过 `tidb_read_staleness` 设置时间窗口后,TiDB 保证读到的时间戳之前的数据一定是完整且一致的。用户可根据业务对延迟的容忍度灵活设置时间窗口。
Q2:Follower Read 会增加 TiKV 的负担吗?
TiKV 节点本身已存储全量数据副本,读请求路由到 Follower 不会增加存储开销。但会增加 Follower 的 CPU 和 I/O 负载,建议根据实际负载监控各节点的资源使用情况。TiDB 的 PD 调度器会自动均衡读流量。
Q3:TiDB 的读写分离需要修改应用代码吗?
Stale Read 只需在 Session 级别设置一次 `tidb_read_staleness`,后续所有 SELECT 自动路由到 Follower,对应用代码几乎无侵入。如不设置,默认走 Leader Read,完全兼容现有 MySQL 应用。
Q4:读写分离方案如何与事务配合?
在事务中,所有读操作默认走 Leader 以保证事务一致性。如需在事务中使用 Follower Read,需注意隔离级别的影响。建议对一致性要求不高的大范围查询(如报表、统计)使用 Follower Read,关键事务保持 Leader Read。
Q5:TiDB 支持跨地域读写分离吗?
TiDB 支持 Placement Rules,可将 Follower 副本放置在异地机房,实现就近读取。结合 TiCDC 跨地域同步或 TiDB 的跨数据中心部署方案,可实现全局读写分离架构,降低跨地域访问延迟。