哈希(Hashed)索引
哈希索引使用被索引字段值的哈希值维护条目。
哈希索引支持使用哈希分分片键的分片。基于哈希的分片 使用字段的哈希索引作为分片键,在分片集群中对数据分区。
使用哈希分片键对collection进行分片可以使数据分布更均匀。有关更多详细信息,请参阅哈希分片。
哈希函数
哈希索引使用哈希函数来计算索引字段值的哈希值。 [1]哈希函数会折叠嵌入式文档并计算整个值的哈希值,但不支持多键(即 数组)索引。 具体来说,在包含数组的字段上创建哈希索引或尝试将数组插入哈希索引字段会返回错误。
提示
在使用哈希索引解析查询时,MongoDB 会自动计算哈希值。应用程序无需计算哈希值。
[1] | 从版本4.0开始, mongosh 提供方法convertShardKeyToHashed() 。 此方法使用与哈希索引相同的哈希函数,可用于查看某个键的哈希值。 |
创建哈希索引
要创建哈希索引,请将hashed
指定为索引键的值,如以下示例所示:
db.collection.createIndex( { _id: "hashed" } )
创建复合哈希索引
4.4 版本新增。
从 MongoDB 4.4 开始,MongoDB 支持创建包含单个哈希字段的复合索引。要创建复合哈希索引,请在创建索引时将hashed
指定为任何单个索引键的值:
db.collection.createIndex( { "fieldA" : 1, "fieldB" : "hashed", "fieldC" : -1 } )
复合哈希索引需要将 featureCompatibilityVersion 设置为 4.4
。
Considerations
嵌入式文档
哈希函数会折叠嵌入式文档并计算整个值的哈希值,但不支持多键(即 数组)索引。 具体来说,在包含数组的字段上创建哈希索引或尝试将数组插入哈希索引字段会返回错误。
唯一约束
MongoDB 不支持在hashed
索引上指定唯一约束。 相反,您可以创建一个额外的非哈希索引,对该字段具有唯一性约束。 MongoDB 可以使用该非哈希索引来实施字段的唯一性。
2 53 Limit
警告
MongoDB hashed
索引会在行哈希运算之前将浮点数截断为 64 位整数。例如,hashed
索引将为包含值 2.3
、2.2
和 2.9
的字段存储相同的值。为防止冲突,请勿对无法可靠转换为 64 位整数(然后再转换回浮点数)的浮点数使用 hashed
索引。MongoDB hashed
索引不支持大于 2 的浮点值 53 。
要查看某个键的哈希值,请参阅 convertShardKeyToHashed()
。
PowerPC 和263
对于哈希索引,MongoDB 4.2 确保 PowerPC 上浮点值 2 63的哈希值与其他平台一致。
虽然可能包含大于 2 53 的浮点值的字段上的 哈希索引 是不支持的配置,但客户端仍可在索引字段具有值 2 63 的位置插入文档。
要列出部署中所有集合的所有hashed
索引,可以在mongosh
中使用以下操作:
db.adminCommand("listDatabases").databases.forEach(function(d){ let mdb = db.getSiblingDB(d.name); mdb.getCollectionInfos({ type: "collection" }).forEach(function(c){ let currentCollection = mdb.getCollection(c.name); currentCollection.getIndexes().forEach(function(idx){ let idxValues = Object.values(Object.assign({}, idx.key)); if (idxValues.includes("hashed")) { print("Hashed index: " + idx.name + " on " + d.name + "." + c.name); printjson(idx); }; }); }); });
要检查索引字段是否包含值 2 63 ,请对collection和索引字段运行以下操作:
如果索引字段类型是标量而不是文档:
// substitute the actual collection name for <collection> // substitute the actual indexed field name for <indexfield> db.<collection>.find( { <indexfield>: Math.pow(2,63) } ); 如果索引字段类型是文档(或标量),则可以运行:
// substitute the actual collection name for <collection> // substitute the actual indexed field name for <indexfield> db.<collection>.find({ $where: function() { function findVal(obj, val) { if (obj === val) return true; for (const child in obj) { if (findVal(obj[child], val)) { return true; } } return false; } return findVal(this.<indexfield>, Math.pow(2, 63)); } })