如何使用$nin、$in等运算符提高Mongodb的查询性能

如何使用$nin、$in等运算符提高Mongodb的查询性能,mongodb,tagging,Mongodb,Tagging,我有一个相当大的数据集,包含超过300万个文档,这些文档具有类似StackOverflow的标记,每个问题都使用标记。我用于存储标记的模式如下所示: {"id": 12345, "tags":["tag1", "tag2", "tag3"]}, {"id": 12346, "tags":["tag2", "tag3"]} 我在标记字段上创建了一个多键索引。当我使用$in或$nin运算符执行查询以查找标记的交集、并集时,在服务器类机器上的性能大约为7秒。我能做些什么来提高查询搜索的速度吗 编辑1

我有一个相当大的数据集,包含超过300万个文档,这些文档具有类似StackOverflow的标记,每个问题都使用标记。我用于存储标记的模式如下所示:

{"id": 12345, "tags":["tag1", "tag2", "tag3"]}, {"id": 12346, "tags":["tag2", "tag3"]}
我在标记字段上创建了一个多键索引。当我使用$in或$nin运算符执行查询以查找标记的交集、并集时,在服务器类机器上的性能大约为7秒。我能做些什么来提高查询搜索的速度吗

编辑1:

这是我们要求的解释计划。我观察到,在我重新启动服务器并只运行mongodb服务器之后,查询返回的速度要快得多。查询执行得更快(<50ms)。我怀疑索引没有缓存在内存中,尽管我有足够的未使用ram可用,并且我的索引(800MB)可以很容易地放入内存中

find({“tags”){$in:['tag1','tag2'],$nin:['tag4',' tag5','tag6','tag7']}); { “游标”:“BtreeCursor标记\u 1 multi”, “未扫描”:6145193, “nscannedObjects”:6145192, “n”:969386, “米利斯”:19640, “NYELDS”:0, “跳过”:0, “isMultiKey”:没错, “indexOnly”:错误, “指数边界”:{ “标签”:[ [ “tag1”, “tag1” ], [ “tag2”, “tag2” ] ] } }


注意

使用
db.col.stats()
检查多键标记索引的大小。如果它不适合RAM,那么您可能会被磁盘绑定,并导致一些磁盘IO成本。如果索引完全放在内存中,那么除了投入更多的硬件之外,我不确定你还能做什么,除非你能优化查询本身


您是否需要搜索所有数据,或者是否可以查询由另一个索引字段过滤的子集?或者,您可以消除
$nin
查询,因为必须迭代每个标记,而as$in只需迭代,直到找到匹配项。

使用
db.col.stats()
检查多键标记索引的大小。如果它不适合RAM,那么您可能会被磁盘绑定,并导致一些磁盘IO成本。如果索引完全放在内存中,那么除了投入更多的硬件之外,我不确定你还能做什么,除非你能优化查询本身


您是否需要搜索所有数据,或者是否可以查询由另一个索引字段过滤的子集?或者,您是否可以消除
$nin
查询,因为必须迭代每个标记,而as$in只需迭代直到找到匹配项,因此查询速度往往会较慢。

这就是我认为的优化(尽管您可能需要对其进行测试)

  • 不要存储标签,而是存储一个小密钥,该密钥标识特定文档具有的所有标签。 比如说post#125的标签是:PHP、MongoDb、数据库

    a) 清洁标签,如将所有标签转换为小盒子等 然后按字母顺序排列。 当前的标签将是:数据库、mongodb、php

    b) 有一个单独的集合,用于存储整数到标记的映射:

    {u id:1,t:“mongodb”}

    {“_id”:2,“t”:“php”}等存储网站的所有可能标记

    c) 要存储文档,请使用标记创建标记键,以对上一个集合中的映射进行编号。 所以现在的数据库、mongodb、php将变成类似于1-12-2的东西

    d) 将您的文档存储为:

    {“id”:12345,“标记”:[1,12,3]}

  • 查询: 在索引字段上使用整数而不是字符串将大大减少索引大小,并且与字符串索引相比,查询速度更快。
    不确定性能提升的数量,但仍值得尝试与当前实现进行比较。

    这就是我认为的优化(尽管您可能需要测试它)

  • 不要存储标签,而是存储一个小密钥,该密钥标识特定文档具有的所有标签。 比如说post#125的标签是:PHP、MongoDb、数据库

    a) 清洁标签,如将所有标签转换为小盒子等 然后按字母顺序排列。 当前的标签将是:数据库、mongodb、php

    b) 有一个单独的集合,用于存储整数到标记的映射:

    {u id:1,t:“mongodb”}

    {“_id”:2,“t”:“php”}等存储网站的所有可能标记

    c) 要存储文档,请使用标记创建标记键,以对上一个集合中的映射进行编号。 所以现在的数据库、mongodb、php将变成类似于1-12-2的东西

    d) 将您的文档存储为:

    {“id”:12345,“标记”:[1,12,3]}

  • 查询: 在索引字段上使用整数而不是字符串将大大减少索引大小,并且与字符串索引相比,查询速度更快。
    不确定性能提升的数量,但仍然值得尝试与您当前的实现进行比较。

    如果您希望性能超快并且没有空间限制,我建议使用视频id数组单独收集标签,并在标签名称上建立索引

    如果您希望性能超快且没有空间限制,我建议使用视频id数组单独收集标签,并在标签名称上建立索引

    这里是另一个建议,但我还没有机会测试它

    { 
        tags:{ 
            items:[ 'a', 'b', 'c' ],
            mixed:{
                a:1, // hash value for a tag
                b:2, // hash value for b tag
                c:3  // hash value for c tag
            }
        }
    }
    
    搜索查询是

    db.demo.find({ 'tags.mixed.a':1, 'tags.mixed.b':2  })
    

    如果可能,我必须为
    标签创建复合索引。混合

    这里是另一个建议,但我还没有机会测试它

    { 
        tags:{ 
            items:[ 'a', 'b', 'c' ],
            mixed:{
                a:1, // hash value for a tag
                b:2, // hash value for b tag
                c:3  // hash value for c tag
            }
        }
    }
    
    搜索查询是

    db.demo.find({ 'tags.mixed.a':1, 'tags.mixed.b':2  })
    
    如果可能,您必须创建com