第 10 章 · 数据架构:分库分表与读写分离
本章讲解 垂直/水平扩展、路由规则、全局 ID、读写一致性与小紫 PaaS 落地,含 可手算路由例题。
前置:ch05 一致性、ch08 案例、ch09 容量;PaaS ch02 MySQL。
10.1 何时拆分
| 信号 | 阈值 | 优先动作 |
|---|---|---|
| 单表行数 | >3000~5000万 | 水平分表 |
| 单库容量 | >500GB~1TB | 垂直分库/分片 |
| 写 TPS | >2000~3000 | 分片或异步化 |
| 慢查询 | 优化后P99>100ms | 读写分离 |
| 连接数 | >max×70% | 读库/ProxySQL |
决策顺序:垂直分库 → 读写分离 → 归档冷数据 → 水平分片。
10.2 垂直分库
shop_user ── 用户、地址
shop_catalog ── 商品、SKU
shop_order ── 订单、订单项
shop_payment ── 支付、退款
| 优点 | 缺点 |
|---|---|
| 与微服务边界一致 | 跨库 join 需应用聚合 |
| 故障隔离 | 分布式事务上升 |
# order-api ConfigMap
data:
DB_ORDER_HOST: mysql-order.prod-middleware.svc.cluster.local
DB_USER_HOST: mysql-user.prod-middleware.svc.cluster.local
| 跨库场景 | 正解 |
|---|---|
| 订单页商品名 | 下单冗余 product_name 快照 |
| 用户订单列表 | order 表存 user_id+昵称快照 |
| 支付对账 | T+1 数仓宽表(ch11) |
10.3 水平分表与路由
拓扑:4 库 × 16 表 = 64 物理表
db_index = user_id % 4 → shop_order_0..3
table_index = user_id % 16 → t_order_00..15
路由例题
| user_id | 计算 | 落点 |
|---|---|---|
| 10086 | 10086%4=2, %16=6 | shop_order_2.t_order_06 |
| 88888801 | %4=1, %16=1 | shop_order_1.t_order_01 |
| 123456789 | %4=1, %16=5 | shop_order_1.t_order_05 |
分片策略
| 策略 | 适用 | 注意 |
|---|---|---|
| Hash 取模 | 均匀 | 扩容需迁移(ch11) |
| Range 时间 | 按月订单 | 当月热点 |
| 一致性 Hash | 大规模 | 实现复杂 |
为何用 user_id:用户订单列表占查询 95%;order_id 分片会导致列表扫全片。
10.4 读写分离
应用 → ProxySQL:6033 → Master(写) / Slave×N(读)
| 规则 | 说明 |
|---|---|
| 写主 | INSERT/UPDATE/DELETE |
| 读从 | 列表、统计(接受秒级延迟) |
| 写后读 | 下单后查详情 走主 或 Redis |
| 事务内 | 全部走主 |
INSERT INTO mysql_query_rules (rule_id,active,match_pattern,destination_hostgroup,apply)
VALUES (1,1,'^INSERT|UPDATE|DELETE',10,1),
(2,1,'^SELECT',20,1);
延迟例题
| 写TPS | 从库延迟 | 读从风险 |
|---|---|---|
| 200 | 0.3s | 低 |
| 800 | 2~5s | 下单后列表缺单 |
| 2000 | 10s+ | 必须写后读主 |
监控:Seconds_Behind_Master>5 → P1,读切主或降级。
env:
- name: DB_WRITE_URL
value: mysql://master:3306/shop_order
- name: DB_READ_URL
value: mysql://slave:3306/shop_order