【问题标题】:Mongodb - aggregation and batchSize - cursor not found after 1 minuteMongodb - 聚合和batchSize - 1分钟后未找到游标
【发布时间】:2018-01-29 01:28:17
【问题描述】:

我有一个不寻常的 mongodb 游标超时问题 - 即使没有达到 10 分钟的超时时间,我也会收到 cursor not found 错误。

我的环境:

  • ubuntu 14.04 LTS
  • PHP 7.1.13
  • libmongoc 捆绑版本 1.8.2
  • MongoDB 扩展版本 1.3.3
  • mongodb/mongodb 1.2.0

这是一些代码。简而言之,我对数百万用户进行循环,对每个用户执行一次大型聚合,用游标遍历结果,然后运行一堆批量更新。

<?php

/**
 * THE AIM IS TO PERFORM A BIG AGGREGATION ON AN ENTIRE COLLECTION, MASSAGE THE RESULTS, AND WRITE INTO A PERMANENT COLLECTION.
 *
 * THE AGGREGATION IS BIG, SO IT WRITES TO DISK, AND WE ACCESS WITH A CURSOR.
 *
 * IN ORDER TO IMPROVE PERFORMANCE, WE BULK-UPSERT INTO THE PERMANENT COLLECTION ON EVERY 10k DOCUMENTS.
 *
 */
class Analytics
{
    const MAX_CURSOR_PAGE_SIZE = 10000;

    public function performAnalytics($arr_user_ids)
    {
        foreach ($arr_user_ids as $item_user_id)
            $this->performUserAnalytics($item_user_id->id);
    }

    private function performUserAnalytics($userId)
    {
        // AGGREGATION QUERY
        $userItemsPage = $this->getUserItemsPage($userId);

        // BULK UPSERT
        $arrItemOps = [];
        foreach ($userItemsPage as $item_user)
        {
            array_push($arrItemOps, [
                'updateOne' => [
                    [ 'my_key' => $item_user->_id, ],
                    [ '$set' => [ 'item_ids' => $item_user->item_ids ] ],
                    [ 'upsert' => true ]
                ]
            ]);

            // BULK-UPSERT FOR PERFORMANCE
            if(count($arrItemOps) > self::MAX_CURSOR_PAGE_SIZE)
            {
                Log::info("BULK-UPSERTING USER ITEMS");
                $permanent_collection->bulkUpsert($arrItemOps);
                $arrItemOps = [];
            }
        }

        // FINAL BULK-UPSERT
        if(count($arrItemOps) > 0)
            $permanent_collection->bulkUpsert($arrItemOps);
    }

    // BIG AGGREGATION
    private function getUserItemsPage($userId)
    {
        return $items_collection->aggregate([
            [
                '$match' => [
                    'user_id' => $userId
                ]
            ],
            [
                '$group' => [
                    '_id' => '$user_id',
                    'item_ids' => [
                        '$addToSet' => '$item_id'
                    ]
                ]
            ]
        ],
        [ 'allowDiskUse' => true ]);
    }
}

代码运行大约需要 2 天。 1.3 天后,我在 foreach ($userItemsPage as $item_user) 行上收到 cursor not found 错误。 让我头疼的是,我看到了一个:

批量替换用户项目

每 50 秒记录一行(45 秒循环超过 10k 个结果,5 秒循环插入),然后在 1 分钟后出现错误行。 每个用户分析需要 1-120 分钟。我没有修改 101 个文档的默认批量大小。

所以我很难看到超时错误隐藏在哪里。

我在这里查看了@Danziger 的出色回复(MongoDB - Error: getMore command failed: Cursor not found),但这似乎与我的情况无关。

还有另一个相关但未回答的问题 (MongoDB - PHP - MongoCursorException 'Cursor not found')

谢谢,

【问题讨论】:

  • 看到零星的崩溃,这次是在 5 小时后,在不同的用户上
  • 我用 'getMore' 命令看不到很多 mongod 日志行。我怀疑 mongo 可能没有使用 101 作为默认批量大小,而只是 initial 批量大小。也许这是find()aggregate()之间的区别

标签: php mongodb timeout cursor aggregation-framework


【解决方案1】:

好的,我想我明白现在发生了什么,find()aggregate()batchSize 的行为不同

查找 (https://docs.mongodb.com/manual/reference/method/cursor.batchSize):

Find

指定每批返回的文档数

Aggregate:

指定光标的初始批量大小

所以对于aggregate(),为后续批次指定batchSize 是在返回的游标上完成的,而不是在聚合命令中。然而,这在 PHP 中是不可能的——我们对光标对象没有相同级别的控制。这意味着在 PHP 中无法控制聚合 batchSize。我为此向 mongo (https://jira.mongodb.org/browse/PHPLIB-312) 的优秀人员提出了一张票。

在我的情况下,这表现为 mongo 向我返回了一个大批量(约 130k 文档),这可能需要 10 分钟以上的时间来处理。我一次插入 10k 个文档,但这并不意味着我一次从 mongo 中读取 10k 个批次。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-05-28
    • 2020-08-10
    • 2018-03-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-30
    相关资源
    最近更新 更多