34

在 mongodb 中有多种类型的index。对于这个问题,我对可用于排序的升序(或降序)索引根据文档“主要与分片集群一起使用以支持散列分片键”(来源)确保“更多数据分布均匀”(来源

我知道你不能像这样创建索引:db.test.ensureIndex( { "key": "hashed", "sortOrder": 1 } )因为你得到一个错误

{
    "createdCollectionAutomatically" : true,
    "numIndexesBefore" : 1,
    "errmsg" : "exception: Currently only single field hashed index supported.",
    "code" : 16763,
    "ok" : 0
}

我的问题:

指数之间:

  1. db.test.ensureIndex( { "key": 1 } )

  2. db.test.ensureIndex( { "key": "hashed" } )

对于查询db.products.find( { key: "a" } ),哪个性能更高?是hashed关键O(1)


我是如何回答这个问题的:

在我知道你不能使用 多键索引之前hashed,我创建了一个表单的索引db.test.ensureIndex( { "key": 1, "sortOrder": 1 } ),并且在创建它时我想知道散列索引是否比升序索引更高效(散列通常是O(1))。我留下了现在的钥匙,因为(正如我上面提到的)db.test.ensureIndex( { "key": "hashed", "sortOrder": 1 } )是不允许的。但是问题是哈希索引更快地通过键搜索仍然留在我的脑海中。

我做索引的情况是:

我有一个集合,其中包含按键分类的排序文档列表。

例如 {key: a, sortOrder: 1, ...}, {key: a, sortOrder: 2, ...}, {key: a, sortOrder: 3, ...}, {key: b, sortOrder: 1, ...}, {key: b, sortOrder: 2, ...}, ...

由于我使用key分类和 sortOrder 进行分页,所以我总是使用一个值来查询过滤,key并使用sortOrder文档的顺序。

这意味着我有两个可能的查询:

  • 对于第一页db.products.find( { key: "a" } ).limit(10).sort({"sortOrder", 1})
  • 对于其他页面db.products.find( { key: "a" , sortOrder: { $gt: 10 } } ).limit(10).sort({"sortOrder", 1})

在这种特定情况下,搜索O(1)键和O(log(n))sortOrder 是理想的,但这是不允许的。

4

2 回答 2

18

对于查询db.products.find( { key: "a" } ),哪一个性能更高?

鉴于该字段key在两种情况下都被索引,复杂性索引搜索本身将非常相似。由于 的值a将被散列,并存储在索引树中。

a如果我们正在寻找总体性能成本,哈希版本会在匹配索引树中的值之前产生额外的(可忽略不计的)哈希值成本。另见mongo/db/index/hash_access_method.h

此外,散列索引将无法利用索引前缀压缩 (WiredTiger)。索引前缀压缩对于某些数据集特别有效,例如具有低基数的数据集(例如,国家/地区),或具有重复值的数据集,例如电话号码、社会保障代码和地理坐标。它对于复合索引特别有效,其中第一个字段与第二个字段的所有唯一值重复。

有什么理由不在无序字段中使用哈希?

通常没有理由散列非范围值。要选择分片键,请考虑值的基数频率变化率

散列索引通常用于分片的特定情况。当一个分片键值是一个单调递增/递减的值时,数据的分布很可能只进入一个分片。这是散列分片键能够改善写入分布的地方。极大地改进分片集群是一个小小的权衡。另请参阅散列与远程分片

是否值得在文档中插入随机散列或值,并将其用于分片而不是在 _id 上生成的散列?

是否值得,取决于用例。自定义散列值意味着对散列值的任何查询都必须通过自定义散列代码,即应用程序。

利用内置散列函数的优点是 MongoDB 在使用散列索引解析查询时会自动计算散列。因此,应用程序不需要计算散列。

于 2017-05-04T02:15:03.853 回答
1

在特定类型的使用中,索引会更小!

是的!在满足以下所有三个条件的非常特定的场景中。

  • 您的访问模式(您的搜索方式)必须仅查找具有特定索引字段值的文档(键值查找,例如,按 SKU 查找产品,或按 ID 查找用户等)
  • 您不需要对索引字段进行基于范围的查询或排序。
  • 您的字段是一个非常大的字符串,并且 Mongo 的字段数字哈希小于原始字段。

例如,我创建了两个索引,对于散列版本,索引的大小更小。这可以提高内存和磁盘利用率。

// The type of data in the collection. Each document is a random string with 65 characters.
{
  "myLargeRandomString": "40a9da87c3e22fe5c47392b0209f296529c01cea3fa35dc3ba2f3d04f1613f8e"
}

指数约为普通版的1/4!

mongos> use MyDb
mongos> db.myCollection.stats()["indexSizes"]
{
    // A regular index. This one is sorted by the value of myLargeRandomString
    "myLargeRandomString_-1"     : 23074062336,

    // The hashed version of the index for the same field. It is around 1/4 of the original size.
    "myLargeRandomString_hashed" : 6557511680,
}

笔记:

如果您已经将_id其用作文档的外键,则这无关紧要,因为_id默认情况下集合将具有索引。与往常一样,对数据进行自己的测试,以检查此更改是否真正使您受益。在此类索引的搜索能力方面存在重大权衡。

于 2021-07-16T23:34:10.463 回答