先说结论
分库分表用于单机容量、写入吞吐或维护窗口已成为明确瓶颈的场景。核心是选择能让主要请求单分片完成且分布均匀的分片键,同时设计路由、扩容、数据迁移、全局 ID 和跨分片查询方案。
它显著增加研发和运维复杂度,不应只因数据“可能变多”提前引入。
分片策略
按用户或租户哈希分片分布均匀,适合点查但扩容会移动数据;按时间或范围分片便于归档,却可能产生热点。实际系统可使用虚拟槽、一致性映射或逻辑分片降低扩容影响。
先确认是否真的需要分片
分库分表前应排除更低成本手段:修复慢 SQL 和索引、归档冷数据、拆分读模型、读写分离、提升单机规格、合理分区、异步化批处理。只有在单库写吞吐、磁盘容量、备份恢复窗口或单表维护成本有明确证据时,才进入分片设计。
发现瓶颈 -> 用真实数据压测验证 -> 先做单机优化
-> 明确容量和增长曲线 -> 选择分片键
-> 设计路由/迁移/查询降级 -> 灰度上线
“数据量过亿”不是充分理由。几十亿行的时间序列表可能通过归档和索引稳定运行;很小但写入极热的租户表也可能需要早期拆分。
分片键评审清单
一个候选分片键要同时回答:
- 最主要的读写请求是否都带该键,能否单分片路由?
- 数据与 QPS 是否均匀,头部租户或热门用户有多大?
- 是否需要跨键 JOIN、事务、唯一约束、排序或聚合?
- 未来增加节点时,预计移动多少数据,迁移期间怎么读写?
- 数据删除、归档、合规擦除是否可以按该键执行?
按 tenant_id 分片常让绝大多数租户请求单分片完成,但超级租户会成为热点;按 order_id 哈希均匀,但“查询某用户所有订单”会跨分片。应从最重要、最频繁的访问模式出发,而不是从表的主键名称出发。
路由与虚拟分片
int logicalShard = hash(tenantId) & (VIRTUAL_SHARD_COUNT - 1);
DatabaseTarget target = routingTable.lookup(logicalShard);
先映射到较多逻辑槽,再由路由表映射到物理库,可以在扩容时迁移部分槽,而不改变所有业务代码的取模规则。路由表必须版本化、可缓存、可回滚,并在请求日志中记录实际 shard,方便排障。
不要把简单 id % 16 写死在多处代码。扩到 32 时映射规则变化,历史数据位置和新数据位置都会变得难以兼容。
全局 ID 与唯一约束
自增主键只在单库唯一,跨分片需要雪花 ID、号段服务、UUID 或“分片号 + 本地序列”等方案。业务唯一约束也要明确归属:若邮箱需要全局唯一,不能只在每个分片各建一个 unique index;可能需要中心索引表、独立账户域或预留映射服务。
全局 ID 不等于业务幂等键。创建订单重试时,应以请求幂等键或业务键先判断是否已创建,而不是每次生成新 ID。
扩容迁移流程
- 新增目标库、表、索引和校验任务。
- 发布同时支持旧路由和新路由的应用代码。
- 对迁移范围启用双写、变更日志或 CDC,确保增量不丢。
- 按批次回填历史数据,校验行数、校验和和关键聚合。
- 让读取逐步切到新位置,处理双读不一致与回退。
- 观察稳定后停止旧写入、保留回滚窗口,再清理旧数据。
每一步都需要幂等。双写失败时不能简单忽略,要记录待修复事件;双读若发现差异,应定义哪边是事实源。迁移过程本身往往比“设计分片算法”更决定项目成败。
跨分片查询怎么办
优先避免:把需要聚合的数据冗余到查询域,按租户/时间预聚合,或使用检索/分析系统承接。确实必须跨分片时,限制 fan-out 数量、并行超时、结果大小和排序深度;全局分页和精确总数都可能非常昂贵。
跨分片事务优先重构为单分片事务 + 可靠事件。TCC、Saga 或分布式事务只用于确实无法拆开的少数核心流程,并配套对账和人工处理。
工程设计
路由层必须支持版本化配置和灰度。扩容常采用双写或变更捕获、历史数据回迁、校验、读切换和旧链路下线;每一步都要幂等、可观测和可回滚。
容易踩坑的地方
分片后 JOIN、聚合、分页和唯一约束都会跨节点放大。读写分离解决读吞吐,不解决单表写入和容量;分表也不会自动修复错误索引和低效 SQL。
常见问题
追问:如何处理跨分片事务?
优先调整聚合边界让事务落在单分片;无法避免时根据一致性要求选择可靠消息、Saga 或分布式事务,并配套幂等、补偿和对账。
追问:范围分片和哈希分片怎么选?
范围分片利于时间归档、范围扫描和局部顺序,但热点更明显;哈希分片更均匀,点查友好,但范围查询和归档会扇出。选择取决于主查询与生命周期需求。
追问:超级租户怎么处理?
先量化它的读写、存储与访问模式。可为其单独分片、按其内部二级键再拆分,或建立独立读模型;不能让一个超级租户拖垮所有普通租户的路由策略。
追问:如何验证迁移没有漏数据?
分批校验主键数量、范围校验和、关键字段聚合和抽样明细;持续比对增量日志,双读期记录差异并重放修复,不能只看总行数相同。