查找MongoDB中最大的文档大小

tcomlyy6  于 2023-05-06  发布在  Go
关注(0)|答案(9)|浏览(193)

是否可以在MongoDB中找到最大的文档大小?
db.collection.stats()显示的是平均大小,这并不真正具有代表性,因为在我的例子中,大小可能会有很大的不同。

eni9jsuy

eni9jsuy1#

您可以使用一个小的shell脚本来获取此值。

注意:这将执行全表扫描,在大型集合上会很慢。

let max = 0, id = null;
db.test.find().forEach(doc => {
    const size = Object.bsonsize(doc); 
    if(size > max) {
        max = size;
        id = doc._id;
    } 
});
print(id, max);
bbmckpt7

bbmckpt72#

Mongo 4.4开始,新的聚合运算符$bsonSize返回给定文档编码为BSON时的大小(以字节为单位)。
因此,为了找到大小最大的文档的bson大小:

// { "_id" : ObjectId("5e6abb2893c609b43d95a985"), "a" : 1, "b" : "hello" }
// { "_id" : ObjectId("5e6abb2893c609b43d95a986"), "c" : 1000, "a" : "world" }
// { "_id" : ObjectId("5e6abb2893c609b43d95a987"), "d" : 2 }
db.collection.aggregate([
  { $group: {
    _id: null,
    max: { $max: { $bsonSize: "$$ROOT" } }
  }}
])
// { "_id" : null, "max" : 46 }

这一点:

  • $group将所有项目放在一起
  • $project是文档$bsonSize$max
  • $$ROOT表示我们获取bsonsize的当前文档
ffx8fchx

ffx8fchx3#

注意:这将尝试将整个结果集存储在内存中(从.toArray开始)。小心大数据。请勿在生产中使用!Abishek的答案具有在游标上工作而不是在内存数组中工作的优点。
如果你也想要_id,试试这个。给定一个名为“requests”的集合:

// Creates a sorted list, then takes the max
db.requests.find().toArray().map(function(request) { return {size:Object.bsonsize(request), _id:request._id}; }).sort(function(a, b) { return a.size-b.size; }).pop();

// { "size" : 3333, "_id" : "someUniqueIdHere" }
5tmbdcev

5tmbdcev4#

在MongoDB集合中查找最大的文档可以比使用aggregation framework和关于集合中文档的一点点知识的其他答案快100倍。此外,您将在几秒钟内获得结果,而使用其他方法(forEach,或者更糟,将所有文档发送到客户端)需要几分钟。
您需要知道文档中哪些字段可能是最大的字段- * 您几乎总是知道 *。只有两个实际的1 MongoDB types可以具有可变大小:

  • 数组

聚合框架可以计算每个的长度。请注意,您不会获得数组的字节大小,而是元素的长度。然而,更重要的是 * 哪些 * 异常文档,而不是它们占用的确切字节数。
下面是如何对数组执行的。举个例子,假设我们在一个社交网络中有一个用户集合,我们怀疑数组friends.ids可能非常大(在实践中,你可能应该保持一个单独的字段,如friendsCount与数组同步,但为了举例,我们假设它不可用):

db.users.aggregate([
    { $match: {
        'friends.ids': { $exists: true }
    }},
    { $project: { 
        sizeLargestField: { $size: '$friends.ids' } 
    }},
    { $sort: {
        sizeLargestField: -1
    }},
])

关键是使用$size aggregation pipeline operator。它只适用于数组,那么文本字段呢?我们可以使用$strLenBytes operator。假设我们怀疑bio字段也可能非常大:

db.users.aggregate([
    { $match: {
        bio: { $exists: true }
    }},
    { $project: { 
        sizeLargestField: { $strLenBytes: '$bio' } 
    }},
    { $sort: {
        sizeLargestField: -1
    }},
])

您还可以使用$sum组合$size$strLenBytes来计算多个字段的大小。在绝大多数情况下,20% of the fields will take up 80% of the size(如果不是10/90甚至1/99)和大型字段必须是字符串或数组。
1从技术上讲,很少使用的binData类型也可以具有可变大小。

uyto3xhc

uyto3xhc5#

嗯。。这是个老问题。但是-我想分享一下我的看法
我的方法-使用Mongo mapReduce函数
首先-让我们获得每个文档的大小

db.myColection.mapReduce
(
   function() { emit(this._id, Object.bsonsize(this)) }, // map the result to be an id / size pair for each document
   function(key, val) { return val }, // val = document size value (single value for each document)
   { 
       query: {}, // query all documents
       out: { inline: 1 } // just return result (don't create a new collection for it)
   } 
)

这将返回所有文档大小,尽管值得一提的是将其保存为集合是一种更好的方法(结果是result字段中的结果数组)
其次-让我们通过操作这个查询来获得文档的最大大小

db.metadata.mapReduce
(
    function() { emit(0, Object.bsonsize(this))}, // mapping a fake id (0) and use the document size as value
    function(key, vals) { return Math.max.apply(Math, vals) }, // use Math.max function to get max value from vals (each val = document size)
    { query: {}, out: { inline: 1 } } // same as first example
)

,它将提供一个值等于最大文档大小的结果

简而言之:

你可能想使用第一个例子,并将其输出保存为一个集合(将out选项更改为你想要的集合名称),并对其应用进一步的聚合(最大大小,最小大小等)。

  • -或-*

您可能希望使用单个查询(第二个选项)来获取单个stat(min、max、avg等)。

j2cgzkjk

j2cgzkjk6#

如果你正在处理一个巨大的集合,那么一次将其全部加载到内存中将不起作用,因为你需要比整个集合的大小更多的RAM。
相反,您可以使用我创建的以下包批量处理整个集合:https://www.npmjs.com/package/mongodb-largest-documents
您所要做的就是提供MongoDB连接字符串和集合名称。当脚本完成批量遍历整个集合时,它将输出前X个最大的文档。

bogh5gae

bogh5gae7#

如果您想获得有关记录的更多详细信息(即文件),其具有最大的大小
1.计算尺寸
1.排序基准计算大小
1.限制到所需的记录数
1.(可选)项目只得到你想要的领域。

db.mycollection.aggregate([
    { $addFields: {
        bsonsize: { $bsonSize: "$$ROOT" }
    }},
    { $sort: { bsonsize: -1 }},
    { $limit: 10 },
    { $project: {
        _id: 1,
        bsonsize: 1
    }}
])

注:(性能)
这将扫描整个集合。如果您有非常大的集合,那么它可能会消耗大量的IOPS。当数据库处于最低活动级别时运行。
我的收藏包含8500万条记录,大小为30GB。跑了75秒。

u4vypkhs

u4vypkhs8#

Elad Nana's package启发,但可用于MongoDB控制台:

function biggest(collection, limit=100, sort_delta=100) {
  var documents = [];
  cursor = collection.find().readPref("nearest");
  while (cursor.hasNext()) {
    var doc = cursor.next();
    var size = Object.bsonsize(doc);
    if (documents.length < limit || size > documents[limit-1].size) {
      documents.push({ id: doc._id.toString(), size: size });
    }
    if (documents.length > (limit + sort_delta) || !cursor.hasNext()) {
      documents.sort(function (first, second) {
        return second.size - first.size;
      });
      documents = documents.slice(0, limit);
    }
  }
  return documents;
}; biggest(db.collection)
  • 使用游标
  • 给出limit最大文档的列表,而不仅仅是最大的文档
  • 每隔sort_delta将输出列表排序并剪切为limit
  • 使用nearest作为read preference(如果您在从节点上,您可能还希望在连接上使用rs.slaveOk(),以便能够列出集合)
ssm49v7z

ssm49v7z9#

正如Xavier Guihot已经提到的,Mongo 4.4中引入了一个新的$bsonSize聚合运算符,它可以给予对象的字节大小。除此之外,我只想提供我自己的例子和一些统计数据。

使用示例:

// I had an `orders` collection in the following format
[
  {
    "uuid": "64178854-8c0f-4791-9e9f-8d6767849bda",
    "status": "new",
    ...
  },
  {
    "uuid": "5145d7f1-e54c-44d9-8c10-ca3ce6f472d6",
    "status": "complete",
    ...
  },
  ...
];

// and I've run the following query to get documents' size
db.getCollection("orders").aggregate(
  [
    {
      $match: { status: "complete" } // pre-filtered only completed orders
    },
    {
      $project: {
        uuid: 1,
        size: { $bsonSize: "$$ROOT" } // added object size
      }
    },
    {
      $sort: { size: -1 }
    },
  ],
  { allowDiskUse: true } // required as I had huge amount of data
);

结果,我收到了一份按大小降序排列的文档列表。

属性:

对于总共约3M记录和约70GB大小的集合,上述查询花费了约6.5分钟。

相关问题