MongoDB嵌入式文档与数组子文档的性能比较

18

考虑到最多有100,000个朋友的下面竞争性的模式,我希望找到最适合我的需求的最有效的方案。

Doc1(在user_id上建立索引)

{
"_id" : "…",
"user_id" : "1",
friends : {
    "2" : {
        "id" : "2",
        "mutuals" : 3
    }
     "3" : {
         "id" : "3",
         "mutuals": "1"
    }
   "4" : {
         "id" : "4",
         "mutuals": "5"
    }
}
}

Doc2(在user_id和friends.id上创建的复合多键索引)

{
"_id" : "…",
"user_id" : "1",
friends : [
   {
        "id" : "2",
        "mutuals" : 3
    },
    {
         "id" : "3",
         "mutuals": "1"
    },
   {
         "id" : "4",
         "mutuals": "5"
    }
]}

我似乎找不到任何关于子字段检索效率的信息。我知道Mongo将数据在内部实现为BSON,所以我想知道投影查找是否是二进制O(log n)?

具体来说,给定一个用户ID,查找是否存在带有朋友ID的朋友,那么这两个不同模式下的查询如何比较?(假设上述索引)请注意,返回什么并不重要,只要如果该朋友存在,则返回非空。

Doc1col.find({user_id : "…"}, {"friends.friend_id"})
Doc2col.find({user_id : "…", "friends.id" : "friend_id"}, {"_id":1})

另外一个有趣的问题是$set修饰符的工作原理。对于模式1,给定查询Doc1col.update({user_id : "…"}, {"$set" : {"friends.friend_id.mutuals" : 5}),查找friends.friend_id的工作原理是怎样的?这是一个O(logn)操作吗(其中n是朋友数量)?

对于模式2,查询Doc2col.update({user_id : "…", "friends.id" : "friend_id"}, {"$set": {"friends.$.mutuals" : 5})与上述内容相比如何?


3
采用数组格式(Doc2),因为动态键几乎从来不是正确的方法。另外,不要使用智能引号(这不是合法的语法,而且很难阅读)。 - JohnnyHK
1
我想Doc2可能会使用多达几个字节的额外存储,但正如@JohnnyHK所说,Doc1并不是一个好的方法。相信我,使用Doc1的人提出的问题很多,然后他们意识到必须转移到Doc2才能对其模式执行任何操作... - Sammaye
谢谢你的建议。@Sammaye,为什么Doc2会使用多几个字节的存储空间?你是指索引吗?顺便说一句,智能引号是复制粘贴时出现的错误。 - Nelson Shaw
索引在两者之间应该是相对相同的大小,嗯,我可能错了,实际上我认为对象数组比其他自定义对象的自定义对象更小,无论哪种方式,我认为存储的差异几乎可以忽略不计。 - Sammaye
1
这个问题在这里得到了很好的回答:https://dev59.com/jGsz5IYBdhLWcg3wLEq8。对我来说,关键点似乎是动态键方法不可索引。 - John Greenall
Doc2还可以使用$unwind运行聚合查询以查询数组元素。在Doc1中无法执行此操作。 - Adam Pridmore
1个回答

3

如果主要需求是以良好可控的方式呈现数据,那么推荐使用doc1。使用投影{}, {friends.2 : 1}可以轻松过滤所需数据。

如果您的用例不关心结果请注意,返回的内容并不重要且需要提高获取速度,则doc2是最佳选择。

此外,doc2允许更清晰简洁的语法。

db.doc2.findOne({user_id: 1, friends.id : 2} )

对比
db.doc1.findOne({ $and : [{ user_id: 1 }, { "friends.2" : {$exists: true} }] })

最后要说的是,然而,我们可以在doc1上创建稀疏索引(并使用$exists),但如果你有10万个朋友,则每个朋友都需要一个稀疏索引,这种情况是荒谬的。相反,对于合理数量的条目,比如人口统计学数据(男性、女性)、年龄组(0-10岁、11-16岁、25-30岁等)或更重要的事项(琴酒、威士忌、伏特加等),我们可以创建合适的索引。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接