命名键与数字键 - mongo

时间:2014-01-27 13:22:26

标签: mongodb

想象一下,我们有一个仅用于缓存的Mongo实例。该集合只是以下

的数组转储
_id
key
value
expiration

然而,我们的项目团队中有人。存储为

_id
0
1
2

我们的后端(PHP)知道0 =“key”,1 =“value”,2 =“expiration”。他说,“最好这样做,所以我们不会在Mongo的每个记录中存储一个长的密钥”

这对我来说很有意义,因为每个文档都是自己存储的。但是,使用任何管理工具或尝试在我们的应用程序之外操作我们的数据几乎是不可能的。它看起来像1和0。所以我出去测试了这个。

我制作了一个名为key和numerics的小型Mongo集合。这样做之后。我在他们两个上跑了db.foo.stats()

他们匹配每个统计信息。所以我想我的问题是。如果我们有一个名为VeryLongKeyDescriptiveText的密钥,并且它存储在1000条记录中。这与存储0和相应值的物理大小相同吗? (我的测试是肯定的,但我不明白Mongo是如何做到这一点的。)

我的测试是两个集合(控制和测试)。使用上述两个键值设置。每个集合目前有3个文档,包括名称,一些base64 loren ipsum文本和到期时的unix时间戳。两个集合都具有相同的确切数据,但测试中的键除外( 0,1,2 )而不是(键,值,到期)。以下是stats()的{​​{1}}输出:http://pastebin.com/tTt7VzwQ

3 个答案:

答案 0 :(得分:3)

确实,两种意见之间的数据库大小差异通常是不成比例的,实际上,您可能只看到短字段名称和长字段名称之间1,000条记录可能有1MB的差异。

有时,如果你持续使用长字段名称并且它们相当长,你会开始看到真正的问题。

此问题的识别也可以与文档内容大小相关联。我的意思是,如果你的文件已经很大,那么你就不会注意到太多的变化。

  

他们匹配每个统计数据。

我想说的是,运气不仅仅是短字段名称与长字段名称相同。

你确定两者之间的数据是一样的,只有一个有短,一个有长字段名吗?

我真的看不出expiration2大小相同的物理可能性,我可以看到key0可能相对相同字节数。

答案 1 :(得分:2)

如果扩展测试用例以创建更大的文档,存储差异将变得更加明显。请考虑以下事项:

function createIntFields(j) {
  var document = {};
  for (i = 0; i < j; i++) {
    document[i] = i;
  }
  return document;
}

function createStringFields(j) {
  var document = {};
  for (i = 0; i < j; i++) {
    document["thisIsAVeryLongFieldNamePrefix" + i] = i;
  }
  return document;
}

db.int.drop();
for (i = 0; i < 1000; i++) { db.int.insert(createIntFields(i)); }

db.string.drop();
for (i = 0; i < 1000; i++) { db.string.insert(createStringFields(i)); }

统计数据确实有所不同(我删除了一些不相关的输出字段):

> db.int.stats();
{
    "ns" : "test.int",
    "count" : 1000,
    "size" : 9395008,
    "avgObjSize" : 9395,
    "storageSize" : 11182080,
    "numExtents" : 6,
    "lastExtentSize" : 8388608
}
> db.string.stats();
{
    "ns" : "test.string",
    "count" : 1000,
    "size" : 32098752,
    "avgObjSize" : 32098,
    "storageSize" : 37797888,
    "numExtents" : 8,
    "lastExtentSize" : 15290368
}

为了解释您在小文档尺寸中看到的内容,我们可以参考Mathias Stearn的storage internals presentation,特别是slide #25。每个记录(例如,在这种情况下为文档)对于记录长度,范围和下一个/上一个指针具有16字节的开销。除此之外,文档的最小有效负载为32字节。因此,即使我们用非常小的文档填充集合:

db.foo.drop();
for (i = 0; i < 1000; i++) { db.foo.insert({_id:i}); }

统计信息将显示平均文档大小为48:

> db.foo.stats()
{
    "ns" : "test.foo",
    "count" : 1000,
    "size" : 48032,
    "avgObjSize" : 48,
    "storageSize" : 172032,
    "numExtents" : 3,
    "lastExtentSize" : 131072
}

当文档有效负载超过32个字节时,power-of-two分配将启动,因此您仍然可以看到以圆形块分配的文档。在我的一些测试中,我注意到112是一个共同的平均大小(96 + 16)。

答案 2 :(得分:1)

我的第一个想法是他们已经实现了字段名称的压缩或标记化,但issue似乎仍未解决(截至2014年1月)。由于padding,它们的大小可能相同。由于性能原因,您的收藏中的文档会被填充,因此它们通常可以在不必移动的情况下调整大小。您可以在没有任何填充的情况下尝试compacting集合,看看您现在是否看到了差异。