首页/数据/NoSQL 速查/哈希索引与分片配合
MongoDB99 条

哈希索引与分片配合

哈希索引按值哈希分布:只支持等值匹配,常用于哈希分片让数据均匀散开,不支持范围查询与排序。

NoSQL 速查数据更新于 2026-10-13

是什么

哈希索引(hashed index)先对字段值求哈希再入索引,索引里存的是哈希值而不是原值。它只服务等值匹配:因为哈希之后相邻值不再相邻,范围查询与排序都用不上它。它的主要用途是作为哈希分片键,让写入均匀散开到各个分片,代价是失去数据局部性、按范围查询只能广播到所有分片。

基本信息

创建db.users.createIndex({ user_id: "hashed" })
支持的操作等值匹配($eq、$in);范围查询、排序、正则都不支持
唯一性不能建唯一哈希索引,唯一性要靠显式字段索引保证
复合写法可含一个哈希字段(MongoDB 4.4+),例如 { tenantId: 1, userId: "hashed" }
浮点细节浮点数会先截断为 64 位整数再哈希,小数差异可能被抹掉

典型使用场景

  • 写入热点打散:自增 ID 或时间戳作分片键会让写入集中在最后一段,改用哈希分片可均匀分布
  • 高基数等值查询:按用户 ID、设备 ID、订单号这类唯一标识查询,正好符合哈希索引的能力范围
  • 均匀分摊存储:各分片数据量更平均,便于容量规划
  • 多租户分流:复合哈希索引在租户前缀固定后再对用户维度做哈希
  • 对照实验:怀疑某个集合存在写热点时,用哈希索引验证分布是否改善

创建与验证

创建与验证
db.users.createIndex({ user_id: "hashed" })           # 哈希索引,只服务等值查询
db.users.find({ user_id: "u-1001" }).explain("executionStats")   # 应命中 IXSCAN,索引名形如 user_id_hashed

db.users.createIndex({ tenantId: 1, userId: "hashed" })          # 复合哈希索引(4.4+)
db.users.find({ tenantId: "t1", userId: "u-1001" }).explain()     # 前缀等值加哈希等值才充分命中

db.users.find({ user_id: { $gt: "u-1000" } })                     # 范围条件用不上哈希索引
db.runCommand({ shardCollection: "app.users", key: { user_id: "hashed" } })   # 用于哈希分片

哈希索引能力对照

查询类型能否使用原因
等值匹配能哈希值可直接定位
$in 多值能展开为多个哈希等值查找
范围查询不能哈希破坏顺序,相邻值不相邻
排序不能哈希值排序与业务顺序无关
正则或前缀匹配不能无法预知哈希结果
哈希分片键带来均匀写入,同时意味着按时间范围查询要广播到所有分片:数据分布与查询模式必须一起权衡,常见的折中方案是复合分片键,用前缀承载常用过滤维度。
浮点数字段建哈希索引要谨慎:值会先截断成整数再参与哈希,2.3 与 2.9 可能落到同一个索引条目上,等值查询的结果与直觉不符。

易错点

  • 在哈希索引字段上做范围查询或排序:完全用不上索引,退化成全集合扫描
  • 以为哈希索引能保证唯一:它不做唯一性校验,重复值不会被拒绝,业务唯一性要另建唯一索引
  • 对低基数字段建哈希索引:值域本来就小,哈希后分布改善有限,索引反而增加写入成本
  • 把哈希分片键用在高频范围查询的表上:每次按时间范围查都要广播全部分片,延迟随分片数上升

数据来源与参考

  • MongoDB Manual: Hashed Indexes
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌