MongoDB、Python 和 PyMongo:文档大小太大且 BSONObj 大小无效

MongoDB, Python and PyMongo: Document size too large with BSONObj size is invalid

写入 Mongo 时出现此错误:

OperationalFailure caught
10334
{u'connectionId': 2365, u'code': 10334, u'ok': 1.0, u'err': u'BSONObj size: 17254820 (0xA4490701) is invalid. Size must be between 0 and 16793600(16MB) First element: 0:

这是一个充满字符串和整数的普通文档,在 Python 中构建,但它的大小似乎是 17,25MB。你会怎么做?

数据是这样的:

{ date: new Date(1417996800000), 
  visitors: [ { owner: "AS3320 Deutsche Telekom AG", ip: "82.148.15.23", views: 844 }, 
              { owner: "AS29314 VECTRA S.A.", ip: "173.235.42.25", views: 458 }, 
                ...
            ]
}

那个数组中有很多很多元素,但我很惊讶数量超过了 16MB。

将数组的大小限制为 8500 个元素后,出现此 PyMongo 错误:

$ operator made object too large

我的文档过大而且我的架构很糟糕,显然,另请参阅:Mongodb update with upsert fails

在设计 Mongo 模式时需要考虑很多事情,但通常模式应该反映您使用数据的方式。 MongoDB 博客上的 6 Rules of Thumb for MongoDB Schema Design 文章系列是一个好的开始。

我的第一个想法是将您的文档"inside out" 并存储在一个集合中:

{ date: new Date(1417996800000), owner: "AS3320 Deutsche Telekom AG", ip: "82.148.15.23", views: 844 },
{ date: new Date(1417996800000), owner: "AS29314 VECTRA S.A.", ip: "173.235.42.25", views: 458 },
...

这样您就不会限制每天可以拥有多少唯一用户。如果您在 date 字段上建立索引,按日期查找访问者仍然很有效。

如果您不经常使用 owner 字段,或许也可以将其移至自己的集合中。

{ ip: "82.148.15.23", owner: "AS3320 Deutsche Telekom AG"},
{ ip: "173.235.42.25", owner: "AS29314 VECTRA S.A."},
...

显然,这不是一个明确的答案,但这可能是一个开始。

关于在写入之前检查文档的大小(我认为这不是解决糟糕的架构设计的好方法)。数据由 MongoDB 在内部作为 BSON 处理,因此您可以使用 bson module:

import bson
len(bson.dumps(my_document))

如果您收到 "operator made object too large" 错误,请参阅 this question