哈希索引仅支持单值$eq或单元素$in精确匹配,不支持$gt/$lt等范围查询、多值$in及前缀匹配,因其哈希值无序且抹除原始值大小与前缀关系,浮点数截断还会导致冲突。

当你在MongoDB中对字段创建哈希索引后,执行{ age: { $gt: 25 } }这类范围查询时,数据库无法利用该索引加速,只能退回到全表扫描——这不是配置错误或版本缺陷,而是哈希索引的设计本质决定的。
哈希索引的值是无序的固定整数
哈希索引会把原始字段值(比如字符串"alice"、数字32、日期ISODate("1990-05-12"))统一输入哈希函数,输出一个固定长度的64位整数(如-4827391028374)。这个过程彻底抹除了原始值的大小关系:32和33哈希后可能变成9823471和-7612093,二者毫无数值顺序可言。
而B-tree类范围索引依赖键值有序排列才能做二分查找、区间跳转;哈希值天生离散、随机分布,【MongoDB无法从哈希值推导出原始值的相对大小】,所以$gt/$lt/$gte/$lte等操作根本无法定位起始点和终止点。
哈希函数不保留前缀与范围信息
普通索引中,“abc”、“abcd”、“abx”按字典序相邻存储,正则{ name: { $regex: "^ab" } }能快速定位前缀块。但哈希后,“abc”→1289374,“abcd”→-8723941,“abx”→5561023,三者在哈希空间里完全散开,没有任何聚类性。
这也意味着:即使你查{ email: { $in: ["a@b.com", "c@d.com", "e@f.com"] } },只要$in含两个及以上值,MongoDB就判定为非等值模式,【直接忽略哈希索引】,哪怕每个值本身都是精确匹配。
浮点数截断进一步破坏可比性
对浮点字段建哈希索引时,MongoDB会在哈希前强制截断为64位整数:2.1、2.5、2.9全部变成2,哈希结果完全一致。这已不是“无法比较”,而是多个不同原始值映射到同一哈希桶——【查询2.5将错误命中2.1和2.9的文档】,范围语义彻底失效。
这种截断冲突在大于2⁵³的浮点数上更严重,此时JavaScript Number精度丢失,哈希输入本身就不可靠。
唯一能走哈希索引的查询只有两类
方法一:单个$eq精确匹配db.users.find({ user_id: "U10029384" }) → MongoDB自动计算"U10029384"的哈希值,在哈希索引B-tree中做一次等值查找。
方法二:单元素$in(逻辑等价于$eq)db.users.find({ user_id: { $in: ["U10029384"] } }) → 解析为等值查询,可走索引。
注意:一旦写成{ $in: ["U10029384", "U10029385"] },立即失效。

















