Как ускорить счет на MongoDB View - PullRequest
0 голосов
/ 25 июня 2018

Я пытался выяснить причину, по которой созданный мной вид MongoDB был таким медленным. Представление предназначено для коллекции transactions и возвращает записи с openBalance, превышающим 0. Я также запускаю несколько дополнительных этапов агрегирования, чтобы сформировать данные так, как я хочу.

Чтобы ускорить выполнение представления, он использует индекс для целевой коллекции путем сопоставления с индексированным полем на первом этапе конвейера агрегации представления, например:

// View Stage 1

{ "transactions.details.openBalance" : { "$exists" : true, "$gt" : 0.0 } }

После долгих исследований я установил, что агрегация из представления возвращает данные очень быстро. Что медленно, так это подсчет, который выполняется как часть конечной точки:

let count = await db.collection('view_transactions_report').find().count();

Итак, сейчас я пытаюсь понять, почему счетчик намного медленнее в представлении, чем в базовой коллекции, и что я могу сделать, чтобы ускорить его. Или, может быть, есть альтернативный способ генерации счетчика?

Базовая коллекция имеет около 800 000 записей, но счет возвращается быстро. Но подсчет представления, который возвращает только отфильтрованный набор из 10 000 из этих 800 000 записей, возвращается гораздо медленнее. Что касается специфики, я говорю о 3/4 секунды для возврата счетчика в коллекции, стихов шесть секунд для возврата счетчика в представлении монго.

Итак, во-первых, почему счетчик намного медленнее в представлении (с гораздо меньшим набором данных), чем в базовом наборе, и, во-вторых, что я могу сделать, чтобы определить скорость счета для представления?

У меня есть пара других запросов агрегации, которые я выполняю, чтобы определить totalCustomers и totalOpenBalance, которые также, кажется, работают медленно (см. Код ниже).

Соответствующая часть моего кода функции конечной точки выглядит так:

// previous code

  let count = await db.collection('view_transaction_report').find(search).count();

  let totalCustomers = await db.collection('view_transaction_report').find(search).count({
     $sum: "customer._id"
   });

  let result = {};

  if (totalCustomers > 0) {
    result = await db.collection('view_transaction_report').aggregate([{
        $match: search,
      },
      {
        $group: {
          _id: null,
          totalOpenBalance: {
            $sum: '$lastTransaction.details.openBalance'
          }
        }
      }
    ]).next();
  }

  db.collection('view_transaction_report').find(search).skip(skip).limit(pagesize).forEach(function (doc) {
    docs.push(doc);
  }, function (err) {
    if (err) {
      if (!ioOnly) {
        return next(err);
      } else {
        return res(err);
      }
    }
    if (ioOnly) {
      res({
        sessionId: sessID,
        count: count,
        data: docs,
        totalCustomers: totalCustomers,
        totalOpenBalance: result.totalOpenBalance
      });
    } else {
      res.send({
        count: count,
        data: docs,
        totalCustomers: totalCustomers,
        totalOpenBalance: result.totalOpenBalance
      });
    }
  });

В терминах executionStats это то, что показано для секции queryPlanner сгенерированного представления:

            "queryPlanner" : {
                "plannerVersion" : 1.0, 
                "namespace" : "vio.transactions", 
                "indexFilterSet" : false, 
                "parsedQuery" : {
                    "$and" : [
                        {
                            "transactions.details.openBalance" : {
                                "$gt" : 0.0
                            }
                        }, 
                        {
                            "transactions.destails.openBalance" : {
                                "$exists" : true
                            }
                        }
                    ]
                }, 
                "winningPlan" : {
                    "stage" : "CACHED_PLAN", 
                    "inputStage" : {
                        "stage" : "FETCH", 
                        "filter" : {
                            "transactions.details.openBalance" : {
                                "$exists" : true
                            }
                        }, 
                        "inputStage" : {
                            "stage" : "IXSCAN", 
                            "keyPattern" : {
                                "transactions.details.openBalance" : 1.0
                            }, 
                            "indexName" : "openBalance", 
                            "isMultiKey" : true, 
                            "multiKeyPaths" : {
                                "transactions.details.openBalance" : [
                                    "transactions", 
                                    "transactions.details"
                                ]
                            }, 
                            "isUnique" : false, 
                            "isSparse" : true, 
                            "isPartial" : false, 
                            "indexVersion" : 2.0, 
                            "direction" : "forward", 
                            "indexBounds" : {
                                "transactions.details.openBalance" : [
                                    "(0.0, inf.0]"
                                ]
                            }
                        }
                    }
                }, 
                "rejectedPlans" : [

                ]
            }

1 Ответ

0 голосов
/ 25 июня 2018

В комментариях @Wan Bachtiar упомянул, что «openBalance» выглядит как индекс мультиключа . Для пояснения, да, в целевой коллекции поле "openBalance" является встроенным полем в массиве. Это имеет место, даже если, по мнению, данные формируются таким образом, что «openBalance» является встроенным полем, которое находится вне массива.

Многопользовательский индекс в целевой коллекции - вот где проблема, потому что вместо ситуации документа 1 на 1 Mongo должен просматривать каждый элемент массива, относящийся к этому полю "openBalance", что, логически, значительно увеличивает сканирование время - потому что иногда существует множество элементов массива, относящихся к этому конкретному полю.

После некоторой дальнейшей проверки я понял, что могу решить эту проблему, изменив способ заполнения "openBalance" в нашей коллекции монго через ETL. Сделав это изменение, я смогу сделать "openBalance" стандартным индексом, а не индексом с несколькими ключами, что, в свою очередь, позволит mongo выполнять поиск в гораздо меньшем наборе данных, чтобы вернуть мои значения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...