是什么
哈希索引(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 可能落到同一个索引条目上,等值查询的结果与直觉不符。