Node.js + MongoDB: перформанс транзакций

“Иногда мы платим больше всего за то, что получаем бесплатно.” — А.Эйнштейн

Не так давно в MongoDB версии 4+ появилась поддержка мульти-документных транзакций.

А поскольку наш проект как раз мигрировал на версию 4.2, закономерно возникли вопросы:

  • Что будет с перформансом?
  • На сколько операции замедлятся?
  • Готовы ли мы пожертвовать скоростью ради (хоть какой-то) точности?

При изучении документации и интернетов вопросов только прибавилось:

  • Все ли операции будут замедлены за счет транзакций?
  • На сколько замедлятся комбинации операций?

Давайте попробуем узнать.

Для того, чтобы претендовать хотя бы на какую-то мизерную долю истины придется немного потрудиться.

Для простоты восприятия разделю имплементацию на 3 шага:

  1. Выбор инструментов
  2. Описание комбинаций операций и получения результатов
  3. Анализ результатов

Теперь о каждом шаге отдельно.

Выбор инструментов:

  1. Необходима тестовая MongoDB (реплика с минимальным количеством mongod процессов) и драйвер для нее: mongodb-memory-server, mongodb.
  2. Для простоты измерения времени я выбрал модуль “microseconds”
  3. Для анализа полученных результатов и визуализации используем следующее: ttest, stdlib.

Описание комбинаций операций и получения результатов:

Имплементируем каждую (из основных) отдельную операцию insertOne, updateOne, deleteOne, findOne, insertMany * updateMany * deleteMany * find * и их комбинации insertOne + updateOne + deleteOne, insertOne + updateOne + deleteOne + findOne, insertMany * + updateMany * + deleteMany * insertMany * + updateMany * + deleteMany * + find * с, и без использования транзакций.

Измерить время выполнения каждой операции.

Для примера — insertMany + updateMany + deleteMany с, и без транзакции





Каждую операцию / измерение повторим 300 раз (для анализа будем использовать 100 результатов «посередине», то есть с 101-го по 200-й) ** — назовем это «микроитерациямы» (итерациями отдельных операций или комбинаций).

Теперь, постоянно меняя последовательность, проведем 100 «макроитерации» (1 «макроитерация» = общее количество «микроитараций» * 300) *
* количество 300 выбрано абсолютно эмпирически
** для более полной информации об имплементации приглашаю посетить github репозиторий (ссылка ниже по тексту)

Анализ результатов:

В результате всех итераций мы получили 20000 замеров для каждой операции и комбинации операций (10000 с использованием транзакции, 10000 — без) в виде массивов



Далее необходимо провести некоторые расчеты.

Обрезать результаты, которые явно выпадают за пределы выборки



Вычислить среднее значение



Вычислить стандартное отклонение



Определить существование статистически достоверной разницы между выборками с помощью ttest (подтверждение или опровержение нулевой гипотезы)



С помощью простых графиков визуализируем результаты. Для примера возьмем комбинацию insertMany + updateMany + deleteMany и отдельно insertOne (все остальные результаты будут изложены в текстовом формате в разделе «Выводы»). В результате в сгенерированных html-файлах есть график, название которого соответствует названию операции или комбинации операции (бирюзовым цветом обозначены безтранзакционные итерации, оранжевым — транзакционные). «Is statistically significant» (true / false) говорит о том, была ли вообще какая-то статистически значимая разница. Все остальное — абсолютные и относительные значения в микросекундах и процентах соответственно.





Выводы:

  1. Вообще нет никакой разницы между операциями с использованием транзакций и без: insertMany + updateMany + deleteMany (ищите иллюстрацию выше)
  2. Существует небольшая разница (до 7%): updateMany, find, insertOne + updateOne + deleteOne + findOne, insertMany + updateMany + deleteMany + find
  3. Транзакции проходят медленнее, но не так критично (91%): updateOne, deleteMany, findOne
  4. Транзакции значительно медленнее (от 197% до 792%): insertOne, insertMany, deleteOne, insertOne + updateOne + deleteOne

Для получения дополнительной информации и возможности проверить результаты, запустив сценарии самостоятельно, посетите github.

Спасибо за то, что прочитали.

Не стесняйтесь комментировать, надеюсь, что у нас выйдет хорошая дискуссия.

Также вы можете запустить все это самостоятельно и получить собственные результаты. Будет круто их сравнить

Полезные ссылки:
medium.com/cashpositive/the-hitchhikers-guide-to-mongodb-transactions-with-mongoose-5bf8a6e22033
blog.yugabyte.com/are-mongodb-acid-transactions-ready-for-high-performance-applications
medium.com/@Alibaba_Cloud/multi-document-transactions-on-mongodb-4-0-eebd662ac237
www.mongodb.com/blog/post/mongodb-multi-document-acid-transactions-general-availability
docs.mongodb.com/manual/core/write-operations-atomicity
www.dbta.com/Columns/MongoDB-Matters/Limitations-in-MongoDB-Transactions-127057.aspx
dzone.com/articles/multi-document-transactions-on-mongodb-40
www.dbta.com/Columns/MongoDB-Matters/MongoDB-Transactions-In-Depth-125890.aspx
www.codementor.io/@christkv/mongodb-transactions-vs-two-phase-commit-u6blq7465
docs.mongodb.com/manual/core/read-isolation-consistency-recency
mathworld.wolfram.com/Outlier.html
support.minitab.com/en-us/minitab-express/1/help-and-how-to/basic-statistics/inference/how-to/two-samples/2-sample-t/interpret-the-results/key-results
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама

Комментарии 6

    –1
    Делается отдельный сервис, который занимается обработкой документов. Новый документ при записи помечается, что требуется обработка (и отправляется оповещение о том, что есть документы, которые требуется обработать). Сервис обрабатывает такие документы по очереди, снимает флаг обработки с документа, отправляет оповещение и идет к следующему.
    Смысл в этих транзакциях?)
      0

      смысл в если у тебя: ноу архитектур

        +2

        Это вы лихо от масштабируемости отказались прям сходу. :)
        Один процесс в одном потоке обрабатывает все изменения по-очереди — да, будет работать без всяких транзакций. Только небыстро.

          –1
          можно разделить по типам документов сервисы, можно по пользователям (если данные изолированные)… в общем можно очень много чего придумать :)
          плюс тут в том, что нет блокирующих действий, если очередь вдруг будет большая, значит индикатор работы над документом будет «гореть» чуть дольше.

          p/s тут еще одно неявное преимущество: нет борьбы за базу
          +1

          А что если несколько документов в разных таблицах нужно обработать атомарно? Т.е. так, чтобы до момента X они все были в состоянии K, а в момент X перешли в состояние L? И так, чтобы соседи ни каких промежуточных состояний не видели?

            0
            Соседи должны видеть только конечный результат. Если эти результаты влияют на другие объекты, это все должно обрабатываться одним сервисом.
            По факту да, получится задержка до нескольких секунд (обычно до секунды) от ввода документа, например, до увеличения остатка на складе… Но т.к. другие документы будут обрабатываться этим же сервисом, то они в любом случае увидят уже выполненные движения.
            Вы уверены, что это критично?
            А вот если критично, то вероятно Вам нужна обычная реляционная база.

        Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

        Самое читаемое