本节摘要:当单库写入吞吐或单表行数触到天花板,就要把数据切开:垂直拆分按业务边界分库分表,水平拆分按分片键把同构数据散到多个物理库。本节讲拆分的触发时机、分片键与路由算法的选择、跨分片查询与全局 ID 两大衍生难题。位置:复制解决读扩展之后,突破写容量的手段,也是架构复杂度陡增的分水岭。
评审会必须先拦住"为了简历拆库"的冲动。触发拆分的真实信号只有几类:单表行数过大导致 DDL 与备份以小时计(经验值:B+ 树三层装不下的数亿行)、单库写入 QPS 顶到磁盘与锁的上限(读写分离已救不了写)、单机存储物理上限、以及业务隔离需求(交易与日志必须物理隔离)。判断口诀:读的问题先找索引和缓存,写的问题才谈拆分;量不到,不拆。拆分引入的复杂度——跨分片查询、分布式事务、全局 ID、扩容搬迁——每一项都是长期成本,签收前想清楚。
垂直分库按业务域切:订单库、商品库、用户库各自独立实例,服务边界与数据库边界对齐,这是微服务化的自然结果。垂直分表按访问频率切:商品主表放高频字段,详情大字段挪到扩展表,把缓冲池留给热数据。垂直拆分风险低、收益立竿见影,且不引入跨行事务的复杂度——先垂直后水平是标准次序,很多系统垂直拆完就够用了好几年。
水平拆分把同一张表的数据按某个列的值散到多个库表。三要素:分片键、路由算法、分片数量。分片键的选择标准一句话:让最高频的查询路径带上它。订单系统高频查询是"查我的订单",分片键选 customer_id;SaaS 系统选租户 ID;日志类选时间。

路由算法两种主流:取模哈希(customer_id mod N)数据均匀但扩容要大规模搬迁;范围路由(按时间或 ID 区间)扩容只加新片,但热点集中(最新分片最热)。折中方案是一致性哈希或基因法(让订单 ID 内嵌分片基因,既能按订单查也能按客户查)。分片数量一次规划到位(比如 32 片映射到若干物理库),用逻辑片与物理库解耦,将来扩容只搬逻辑片。
跨分片查询:不带分片键的查询要广播全部分片再归并,如图右下。缓解手段按优先级:二级索引映射表(记录订单 ID 与分片的对应关系)、异构索引库(同步一份数据按查询维度组织,类搜索中间件思路)、冗余双写(订单同时按 customer_id 和 merchant_id 两套分片各写一份)。分布式事务:跨分片写入要么走柔性事务(消息最终一致,3.3 与 6.2 的思路组合),要么用 XA(性能代价大)。评审会标准答案:尽量避免设计出需要跨分片强一致的业务流程,域内闭环是分片友好设计的核心。
全局 ID:自增主键在分片后必然冲突。主流方案:号段模式(应用批量领号段缓存使用)、雪花算法(时间戳加机器号加序列,趋势递增对 B+ 树友好)。注意雪花对时钟回拨敏感,选型时确认中间件的处理策略。
要点回顾:先问要不要拆,读瓶颈不归分片管;先垂直后水平;分片键让高频路径带上它;路由算法在均匀与扩容间权衡;跨片查询与全局 ID 是两笔先算清的账。数据分好了,下一节把整套系统编排成"坏一台也不停服"。
分片方案选定后,真正的考验在扩容。假设订单表按 customer_id 分了 2 片,如今容量告急要扩到 8 片,取模规则从 mod 2 变成 mod 8。
方案一:停机搬迁(最简单,也最不推荐)。 停写 → 全量导出 → 按新规则重新导入 → 校验 → 切流量。整个过程可控,但停机时长等于数据量除以搬迁速度,亿级行通常以小时计,多数业务接受不了。
方案二:双写 + 影子迁移(主流)。 分四步:
每一步都要能回滚:双写可关、同步可停、读流量可切回。整个方案的核心不是搬迁技术,而是每个阶段都有明确的验证手段与回退开关。
方案三:借道中间件。 ShardingSphere、Vitess 一类组件把路由、搬迁、校验封装成工具,能省掉大量自研工作。代价是引入一层依赖与它自身的运维复杂度。评审会上的判断标准:团队有没有能力自研并长期维护这套搬迁逻辑?没有的话,中间件是更理性的选择。
分片键是分库分表里最难改的决定——改键等于重做一次全量搬迁。若发现选错了(比如按 customer_id 分,但高频查询其实是按 merchant_id 查商家订单),有三条补救路径,代价递增:
复盘这类事故的结论往往很朴素:分片键的选择应该由查询分布统计说话。上线前把核心查询 SQL 全量捞一遍,统计每个查询维度的出现频次,占比最高的那个维度就是分片键的候选。凭经验拍脑袋选的键,八成会后悔。