← 每日面试题 学习看板

分表字段如何选择?

面试题/03数据库中间件/04分库分表/分表字段如何选择?.md

面试记录 2026-09-23

题目:分表字段如何选择?

难度:medium

标签:db/sharding, general/knowledge, difficulty/medium

评分:⭐⭐⭐⭐☆(3.5/5)

我的回答

分表字段的选择,首先是考虑业务逻辑,具体使用什么查询。然后另外是看业务数据区分度,怎样可以更好的把数据区分开。常规的分表字段选择,一方面是根据时间,把某些时间范围的,比如一年的数据聚合到一起,或者一个月的数据聚合到一起,一起分成一个表,按月去查询。当然,这是对于常规业务,比如说长有时间跨度的查询,指定会少掉,快速的少到某个分区,分分表数据上的。另外划分的话,也是根据查询业务逻辑,比如说会查询某个用户的某些数据,这时候可能会根据用户的 ID 做哈希,一致性哈希去进行分区。这时少一个用户的数据的时候,这个用户的所有数据在一个分区上可以快速拿,快速的获取。对应的还有类似于买家跟卖家的 ID 作为分区键。这些主要是看业务实现逻辑。另外,如果业务逻辑中包含多种情况,可能就会做额外的选择。比如说做子表,给用户的订单做一个子表,用户的订单跟订单 ID 连一个数据表。然后订单自己做自己的分区,用户的读取数据的时候,先扫到用户包含的订单,再拿到的 ID 去做组件扫描,扫到对应的订单信息,最终得到返回。

点评

优点:

不足:

补充:

完整讲解

第一部分:知识重点剖析

核心考点定位:能否系统评估分片键选择的正确性,并覆盖"多查询路径"这一高频陷阱。

知识点拆解:

  1. 五大核心原则:
  1. 常见分片键选择模式:
  1. 多查询路径的处理(分片键最难的地方):
  1. 典型反例:
  1. 业务侧的隐性成本:

第二部分:考察方向与延伸递进

考察方向:

延伸递进面试题:

  1. 分库键和分表键为什么建议不同?相同会有什么代价?
  1. 一个订单表既要按 user_id 查又要按 order_id 查,怎么设计?
  1. 分片键选错了怎么办?线上如何订正?

第三部分:面试者参考回答(300-600 字口语化)

面试官您好,选分片键是分库分表里最容易出错的一步,因为它一旦定了,改起来就是数据大迁移。我一般用五个原则来评估:

第一是主查询路径命中。分片键要覆盖 80% 以上的核心查询条件。比如订单表,用户端 90% 场景是"我的订单",那 user_id 就是首选。

第二是数据分布均匀。值域要均匀分布,不能让某个值或某段值占大头。所以像 status、city、type 这种低基数字段绝对不能选——大部分订单都是"已支付",一选就倾斜。

第三是字段不可变。分片键一旦定下来,改就是数据迁移。所以选 user_id、order_id 这种稳定标识,不要选 status、address 这种会变动的。

第四是避免 NULL 和异常值。NULL 无法参与哈希,会堆到某个默认分片,制造倾斜。

第五是低基数陷阱。有些字段虽然业务上"看起来可以",但基数太低就注定倾斜——比如 order_type 只有"普通/预售/定金"3 个值,怎么分都不均匀。

多查询路径是分片键最难的点。举个订单表的经典案例:既要按 user_id 查"我的订单",又要按 order_id 查"订单详情"。如果按 user_id 分片,按 order_id 查就要跨所有分片扫——这个代价太大。我会用异构索引表:主表按 user_id 分片存订单详情,另建一张轻量 order_index(order_id, user_id, status) 按 order_id 分片存索引。写订单时双写,查详情时先查索引表拿到 user_id,再回主表精确定位。这个模式在电商是标配。

分库键和分表键经常分开选。分库键选"分布最均匀的字段"来保证负载均衡(比如 user_id),分表键可以在库内做二级分布(比如 hash(user_id, order_id))。这样既保证库间均衡,又保证库内均匀,还降低单表规模。

如果查询主路径特别多怎么办?我会优先考虑把非主查询迁到 ES 或异构库——比如搜索、后台管理这种低 QPS 但查询复杂的场景,走 MySQL 分片反而复杂,ES 更适合。

分片键选错了怎么救?双写新旧分片 → 全量回补 → 灰度切读 → 校验对账 → 反向迁移 → 下线旧分片。全程依赖 ShardingSphere 这类中间件的在线 DDL 能力,业务方能无感切换。这也是为什么分库分表一定要一开始就选对——选错的代价比不做还高。