分库分表常见路由算法有哪些?各有什么优缺点?
简化版
常见路由算法有范围路由、Hash 取模、一致性哈希、映射表路由和复合路由。Range 便于范围查询但容易热点,Hash 取模分布均匀但扩容迁移成本高,一致性哈希降低扩容迁移量,映射表灵活但多一次查询和维护成本。
详细版
常见方案对比如下:
| 路由方式 | 示例 | 优点 | 缺点 |
|---|---|---|---|
| Range | 按时间、ID 区间 | 范围查询友好、归档方便 | 容易写热点、数据倾斜 |
| Hash 取模 | user_id % N | 简单、均匀、点查高效 | 扩容时大量数据重分布 |
| 一致性哈希 | 哈希环 + 虚拟节点 | 扩容迁移量较小 | 实现和运维复杂度更高 |
| 映射表 | biz_id -> shard | 灵活、可定向迁移 | 依赖索引表,需保证一致性 |
| 复合路由 | 时间 + 用户 ID | 兼顾归档和均匀性 | 规则复杂,查询要严格带条件 |
面试时要结合业务场景回答:订单列表常按用户路由,日志流水常按时间 Range,租户系统可用映射表或租户 ID Hash,大规模动态扩容场景可以考虑一致性哈希或分片映射层。
完整版教学
一、路由算法解决的是“请求去哪儿”的问题
分库分表后,应用面对的是逻辑表,比如 order。但数据库里真实存在的是 order_00、order_01、order_02 等物理表,甚至分布在多个库上。路由算法就是把业务字段映射到物理位置的规则。
例如:
库编号 = user_id % 4
表编号 = user_id / 4 % 16
这样一个 user_id 可以被稳定路由到某个库和某张表。只要查询条件带上 user_id,中间件就能精准路由;如果不带,路由层只能广播。
二、Range 路由适合范围查询,但要警惕热点
Range 路由是按区间分片。比如订单按月份建表:order_202607、order_202608;或者用户 ID 1 到 1000 万在分片 A,1000 万到 2000 万在分片 B。
它的好处是范围查询、数据归档和冷热分离很自然。查最近一个月订单,只需要扫最近的月表;归档三年前数据,也可以直接迁移老分片。
问题是写入容易集中。按时间分片时,所有新订单都写当前月表,当前分片压力极大,历史分片几乎不动。按自增 ID 范围分片也类似,最新 ID 总是落在最后一个区间。解决办法通常是把时间与 Hash 结合,比如“按月分表 + 月内按用户取模”,既保留归档便利,又避免所有写入挤到一张表。
三、Hash 取模简单高效,但扩容是硬伤
Hash 取模是最常见的路由方式:
shard = hash(user_id) % 64
它的优点是简单、稳定、分布相对均匀,尤其适合点查和按分片键查询。只要分片键基数足够大,请求会被打散到多个分片。
缺点在扩容。假设从 64 个分片扩到 128 个分片,hash(x) % 64 和 hash(x) % 128 的结果大部分会变,意味着大量数据要搬迁,请求路由也要切换。如果没有平滑迁移方案,很容易出现读旧、写新、重复数据或数据丢失。
工程上常见改进是预分片。比如一开始就设计 1024 个逻辑分片,但物理上先放在 4 个库里。以后扩容只移动一部分逻辑分片到新库,应用的逻辑路由规则不变。
四、一致性哈希降低迁移量,但不是万能解
一致性哈希把哈希空间组织成一个环,节点加入或退出时,只影响环上相邻的一小段数据。虚拟节点可以让数据分布更均匀。它常用于缓存节点、存储节点和部分动态分片场景。
它的优势是扩缩容时迁移量比简单取模小。缺点是实现和运维复杂度更高,而且数据库分库分表不只关心数据迁移,还关心事务、索引、SQL 路由、容量规划和热点治理。很多数据库分片系统不会直接使用纯一致性哈希,而是使用“逻辑分片 + 映射表”的方式获得类似的迁移灵活性。
五、映射表路由最灵活,但要维护元数据一致性
映射表路由是把业务 ID 与分片位置存在一张元数据表里。例如:
tenant_id -> db_03
table_group -> order_12
它适合租户系统、大客户隔离、需要定向迁移的场景。某个租户太大,可以把它迁到独立库,只改映射关系。
代价是每次请求可能要先查路由元数据,或者依赖本地缓存。元数据如果不一致,请求会路由错。通常要给映射表做高可用、本地缓存、版本号、变更发布和回滚机制。
六、路由算法背后的两个核心指标:命中范围和迁移成本
评价一个路由算法,至少要看两个指标。第一个是命中范围:一次查询会命中一个分片、少量分片,还是所有分片。带分片键的 Hash 点查通常命中一个分片;按时间 Range 查询可能命中某几个时间分片;不带关键条件的后台查询可能命中全部分片。命中范围越大,请求放大越严重,尾延迟也越差。
第二个是迁移成本。简单取模在分片数变化时会导致大量 key 重新映射;Range 新增未来区间很容易,但历史数据不均衡时迁移困难;一致性哈希降低节点变化影响,但仍要处理数据库数据搬迁、索引、事务和路由缓存;映射表路由迁移最灵活,但元数据一致性和缓存刷新要求高。
所以路由算法不能只比较“查询快不快”。它还决定扩容时能不能平滑迁移、故障时能不能快速摘除某个分片、运营查询会不会拖垮在线链路。成熟方案常使用逻辑分片加物理映射,把业务路由规则和物理部署解耦。
七、中间件路由和应用路由的取舍
分库分表路由可以放在应用代码里,也可以交给中间件或代理层。应用路由性能直接、逻辑清晰,业务可以灵活控制;缺点是侵入代码,多个服务要保持规则一致。中间件路由对业务更透明,可以统一处理 SQL 改写、结果合并、数据源管理;缺点是复杂 SQL 支持有限,一旦误用容易产生广播。
代理层路由让语言和应用更无感,但多一跳网络,也可能成为性能瓶颈和故障点。客户端 SDK 路由性能更好,但升级和规则发布要治理好。选择哪种方式,要看团队能力、业务复杂度、SQL 复杂度和运维体系。
面试里如果被问到 ShardingSphere、MyCAT、Vitess 这类工具,不必硬背实现细节,但要能说清它们承担的是“逻辑表到物理库表的路由、SQL 改写和结果归并”这件事,同时提醒复杂 Join、子查询、跨分片分页聚合仍然要谨慎设计。
八、常见误区与追问
这道题要紧扣「分片路由算法」本身回答,不能把它混成泛泛的分库分表套话。面试官通常不是只听定义,而是看你能不能把适用场景、关键流程、失败边界和工程取舍串起来,尤其要说明拆分边界、路由规则、扩容迁移、跨库查询和一致性兜底。
| 回答层次 | 要讲清的内容 | 容易漏掉的边界 |
|---|---|---|
| 核心结论 | 路由算法负责把业务 key 映射到库表,常见有取模、范围、一致性哈希和路由表,各自影响扩容、热点和查询效率 | 不要停在名词解释 |
| 流程机制 | 提取分片键 -> 执行路由规则 -> 定位库表 -> 执行 SQL 或请求 -> 合并结果 -> 扩容时更新规则或路由表 | 要说清触发点、状态变化、确认点和失败兜底 |
| 工程取舍 | 按 user_id % 16 路由简单高效,但扩到 32 时大量 key 会重新分布,迁移成本很高 | 分库分表能扩展容量和吞吐,但会带来路由、事务、Join、唯一约束、扩容和运维复杂度 |
分片路由算法 面试拆解:
1. 提取分片键
2. 执行路由规则
3. 定位库表
4. 执行 SQL 或请求
5. 合并结果
6. 扩容时更新规则或路由表
记忆钩子:先给结论,再拆流程,再讲数字例子和失败边界;回答「分片路由算法」时要围绕题目问法收束,不要把相邻概念堆成一段没有重点的名词清单。
- 误区:取模分片永远最好。 取模简单但扩容迁移成本高,也不适合范围查询。
- 误区:范围分片不会热点。 按时间范围容易让最新分片成为写热点。
- 误区:一致性哈希能解决所有迁移。 它降低迁移比例,但数据库分片还要考虑数据搬迁和事务边界。
- 追问:路由表适合什么场景? 适合租户、商户等需要人工迁移和特殊路由的大颗粒对象。
- 追问:没有分片键怎么办? 广播查询、全局索引、搜索系统或改造查询入口。
- 追问:如何避免路由规则混乱? 版本化配置、灰度发布、路由校验和迁移审计。
九、加强记忆
路由算法没有绝对最优,只有和业务访问模式匹配不匹配。Range 记“范围友好但怕热点”,Hash 记“均匀简单但扩容痛”,一致性哈希记“减少迁移但复杂”,映射表记“灵活可控但依赖元数据”。面试时把这四句话和具体业务例子连起来,答案就很稳。