MongoDB:文档大小是否影响查询性能?
假设一个手机游戏由一个MongoDB数据库支持,该数据库包含一个MongoDB:文档大小是否影响查询性能?,mongodb,mongodb-query,Mongodb,Mongodb Query,假设一个手机游戏由一个MongoDB数据库支持,该数据库包含一个用户集合和数百万个文档 现在假设几十个必须与用户关联的属性-例如\u id朋友文档的值数组、它们的用户名、照片、游戏文档的\u id值数组、上次登录日期、游戏内货币计数等 我关心的是,在数百万用户文档上创建和更新大型、不断增长的数组是否会增加每个用户文档的“权重”,和/或增加整个系统的速度 我们可能永远不会对每个文档使用16mb的eclipse,但我们可以放心地说,如果我们直接存储这些不断增长的列表,我们的文档将增加10-20倍 问
用户
集合和数百万个文档
现在假设几十个必须与用户关联的属性-例如\u id
朋友文档的值数组、它们的用户名、照片、游戏文档的\u id
值数组、上次登录日期、游戏内货币计数等
我关心的是,在数百万用户文档上创建和更新大型、不断增长的数组是否会增加每个用户文档的“权重”,和/或增加整个系统的速度
我们可能永远不会对每个文档使用16mb的eclipse,但我们可以放心地说,如果我们直接存储这些不断增长的列表,我们的文档将增加10-20倍
问题:这在MongoDB中是个问题吗?即使使用投影和索引等正确管理查询,文档大小是否也很重要?我们是否应该积极削减文档大小,例如引用外部列表与直接嵌入\u id
值列表?
换句话说:如果我想要一个用户的last\u login
值,那么如果我的user
文档是100kb而不是5mb,那么只投影/选择last\u login
字段的查询会有什么不同吗
或者:如果我想查找具有特定上次登录值的所有用户,文档大小是否会影响此类查询?首先,您应该花一点时间阅读MongoDB如何根据填充因子和大小分配来存储文档:
简单地说,MongoDB在存储原始文档时会尝试分配一些额外的空间,以实现增长。Power2Sizes分配成为2.6版的默认方法,它将以2的幂增加文档大小
总体而言,如果所有更新都符合原始大小分配,则性能会更好。原因是,如果他们不这样做,整个文档需要移动到其他有足够空间的地方,从而导致更多的读写操作,实际上会导致存储碎片化
如果您的文档确实要以10倍到20倍的时间增长,这可能意味着每个文档要进行多次移动,这取决于您的插入、更新和读取频率,可能会导致问题。如果是这种情况,您可以考虑以下几种方法:
1) 在初始插入时分配足够的空间,以覆盖大多数(比如90%)正常文档的生命周期增长。虽然这在开始时会降低空间使用效率,但随着文档的增长,效率会随着时间的推移而提高,而不会降低任何性能。实际上,您将提前支付存储费用,最终在以后使用这些存储以获得良好的性能
2) 创建“溢出”文档—假设一个典型的80-20规则适用,并且80%的文档将适合一定的大小。分配该金额并添加一个溢出集合,例如,如果您的文档有100多个好友或100个游戏文档,则可以指向该集合。溢出字段指向此新集合中的文档,如果溢出字段存在,则应用程序仅在新集合中查找。允许80%的用户进行正常的文档处理,并避免在80%的不需要的用户文档上浪费大量存储空间,从而增加应用程序的复杂性
无论在哪种情况下,我都会考虑通过建立适当的索引来使用覆盖查询:
覆盖查询是一种查询,其中:
all the fields in the query are part of an index, and
all the fields returned in the results are in the same index.
因为索引“覆盖”了查询,所以MongoDB可以同时匹配查询
条件并仅使用索引返回结果;MongoDB有
不需要查看文档,只需查看索引即可完成
询问
仅查询索引可能比查询文档快得多
在索引之外。索引键通常小于
它们编目的文档和索引通常在RAM或
按顺序位于磁盘上
关于这种方法的更多信息:重新表述问题的一种方法是,如果文档分别为16mb和16kb,100万个文档查询是否需要更长的时间
根据我自己的经验,如果我错了,请纠正我,文档大小越小,查询速度越快
我对500k文档和25k文档进行了查询,25k查询速度明显更快,从几毫秒到1-3秒不等。在生产中,时差约为2-10倍
文档大小发挥作用的一个方面是查询排序,在这种情况下,文档大小将影响查询本身是否运行。我已经多次达到这一限制,尝试对多达2k个文档进行排序
以下是一些解决方案的更多参考:
最终,最终受害的是最终用户
当我试图修复导致性能异常缓慢的大型查询时。我通常会发现自己创建了一个包含数据子集的新集合,并使用了大量查询条件以及排序和限制
希望这有帮助 我只是想分享一下我在MongoDB中处理大型文档的经验<不要这样做强>
我们犯了一个错误,允许用户在文档中包含以base64编码的文件(通常是图像和屏幕截图)。我们最终收集了大约500k个文档,每个文档的大小从2MB到10MB不等
在此集合中进行简单聚合将导致集群崩溃
在MongoDB中,聚合查询可能非常繁重,特别是对于这样的大型文档。聚合中的索引只能在某些情况下使用,因为我们需要$group
,所以没有使用索引,MongoDB必须扫描所有文档
在具有较小文档的集合中执行完全相同的查询
const numberOfDocuments = 1024;
// 2MB string x 1024 ~ 2GB collection
const bigString = 'a'.repeat(2 * 1024 * 1024);
// generate and insert documents in two collections: shortDocuments and
// largeDocuments;
for (let i = 0; i < numberOfDocuments; i++) {
let doc = {};
// field a: integer between 0 and 10, equal in both collections;
doc.a = ~~(Math.random() * 10);
// field b: single character between a to j, equal in both collections;
doc.b = String.fromCharCode(97 + ~~(Math.random() * 10));
//insert in smallDocuments collection
db.smallDocuments.insert(doc);
// field c: big string, present only in bigDocuments collection;
doc.c = bigString;
//insert in bigDocuments collection
db.bigDocuments.insert(doc);
}
const numbersToQuery = [];
// generate 100 random numbers to query documents using field 'a':
for (let i = 0; i < 100; i++) {
numbersToQuery.push(~~(Math.random() * 10));
}
const smallStart = Date.now();
numbersToQuery.forEach(number => {
// query using inequality conditions: slower than equality
const docs = db.smallDocuments
.find({ a: { $ne: number } }, { a: 1, b: 1 })
.toArray();
});
print('Small:' + (Date.now() - smallStart) + ' ms');
const bigStart = Date.now();
numbersToQuery.forEach(number => {
// repeat the same queries in the bigDocuments collection; note that the big field 'c'
// is ommited in the projection
const docs = db.bigDocuments
.find({ a: { $ne: number } }, { a: 1, b: 1 })
.toArray();
});
print('Big: ' + (Date.now() - bigStart) + ' ms');
Small: 1976 ms
Big: 19835 ms
Small: 2258 ms
Big: 4761 ms