06.2 - 读写分离与分库分表
定位:读写分离解决"读压力",分库分表解决"数据量增长"——理解两者的适用场景和代价是架构设计的关键 面试高频度:⭐⭐⭐⭐ 考查方式:读写分离的延迟问题、分片键选择、跨分片查询、分布式事务
一、这是什么?为什么需要它?
是什么
- 读写分离:主库处理写操作(INSERT/UPDATE/DELETE),从库处理读操作(SELECT)
- 分库分表:当单库/单表数据量过大时,拆分为多个数据库/表
为什么需要扩展?
数据库的扩展困境:
- 单表数据量 > 1 千万行 → 索引层高增加,写入变慢
- 单库 QPS > 5K → Buffer Pool 竞争,IO 争抢
- 只能纵向扩展(换更好的硬件)→ 成本高且有上限
核心 insight:读写分离解决的是"IO 争抢"问题,分库分表解决的是"数据量极限"问题。但两者都带来复杂性。在采用前,先用索引优化、缓存(Redis)、垂直拆分等更简单的手段。
二、原理拆解
2.1 读写分离
架构
应用层
│
├── 写操作 ──→ Master(主库)
│
└── 读操作 ──→ Slave1, Slave2...(从库)
│
ProxySQL / ShardingSphere / 应用层路由两大挑战
挑战 1:主从延迟导致的"读不到刚写的数据"
sql
-- 用户发表评论
INSERT INTO comments (user_id, content) VALUES (1, '好文章!');
-- 写入主库成功 ✅
-- 立即查询
SELECT * FROM comments WHERE user_id = 1 ORDER BY create_time DESC;
-- 如果查询去了从库 → 可能查不到刚插入的评论!
-- 因为 binlog 还没同步到从库解决方案:
| 方案 | 说明 | 代价 |
|---|---|---|
| 强制读主 | 对一致性要求高的查询走主库 | 增加主库压力 |
| 等待同步 | 写入后等待从库确认同步 | 增加写入延迟 |
| 缓存 | 写入后更新 Redis 缓存,从缓存读取 | 增加维护复杂度 |
挑战 2:路由层的实现
- 应用层:配置多个数据源,代码中决定走主/从(如
@Master@Slave注解) - 中间件:ProxySQL、MyCat、ShardingSphere 等透明代理
2.2 分库分表
垂直分库 vs 水平分表
垂直分库(按业务拆分):
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ user_db │ │ order_db │ │ product_db │
│ ┌──────────┐ │ │ ┌──────────┐ │ │ ┌──────────┐ │
│ │ users │ │ │ │ orders │ │ │ │ products │ │
│ │ user_tag │ │ │ │ order_item│ │ │ │ category │ │
│ └──────────┘ │ │ └──────────┘ │ │ └──────────┘ │
└──────────────┘ └──────────────┘ └──────────────┘
水平分表(按数据拆分):
order_db
├── orders_0 (user_id % 4 = 0)
├── orders_1 (user_id % 4 = 1)
├── orders_2 (user_id % 4 = 2)
└── orders_3 (user_id % 4 = 3)分片键选择
好的分片键标准: 查询均匀分布、>90% 查询带分片键
| 分片键 | 优点 | 缺点 |
|---|---|---|
| user_id | 按用户拆分最自然,用户级查询高效 | 全局查询(如管理员查所有订单)需要扫描所有分片 |
| order_id | 按订单 ID 哈希分布均匀 | 用户查询要路由到所有分片 |
| 时间 | 适合归档(旧数据放到慢盘) | 热点集中在最新分片 |
跨分片查询的代价:
sql
-- 按 user_id 分片
SELECT * FROM orders WHERE user_id = 42; -- ✅ 只需查 1 个分片
-- 不带分片键
SELECT * FROM orders WHERE status = 'PAID'; -- ❌ 需要查所有 4 个分片分库分表的常见问题
| 问题 | 说明 | 解决方案 |
|---|---|---|
| 跨分片 JOIN | 无法跨数据库 JOIN | 应用层聚合 / 宽表冗余 |
| 全局主键 | 分片后自增 ID 冲突 | Snowflake、UUID、号段模式 |
| 分布式事务 | 跨库事务需要 XA | 柔性事务(TCC、Saga)、最终一致性 |
| 分布式排序分页 | ORDER BY ... LIMIT 10 要合并结果 | 查 N 个分片取 TopN 再合并 |
| 数据迁移 | 增加分片数需要 rehash | 一致性哈希、虚拟槽位 |
三、面试视角
| 追问 | 答案要点 |
|---|---|
| 读写分离的主从延迟怎么处理? | 刚写入的强制读主、缓存加速、等待同步 |
| 分库分表的分片键怎么选? | 按访问频率最高的维度分片,>90% 查询带分片键 |
| 分库分表后怎么做 JOIN? | 应用层聚合、宽表反范式化、字段冗余 |
| 分布式 ID 怎么做? | Snowflake(时间戳+机器 ID+序列)、号段模式(数据库批量生成 ID 段) |
| 分库分表后怎么做排序分页? | 查所有分片取 TopN,应用层合并排序 |
| 什么时候应该用分库分表? | 单表 > 1 亿行,且索引优化+缓存+读写分离都无法解决后,才考虑 |
📚 相关链接
- 主从复制:**主从复制原理**
- ← 返回 **高可用与架构索引**