As such, there’s really no “standard” benchmark that will inform you about the best technology to use for your application. Only your requirements, your data, and your infrastructure can tell you what you need to know.

Для начала немного философии. NoSql окружает и от этого никуда не убежать (хотя не очень то и хотелось). Оставим вопросы о глубинных причинах за рамками данного текста, отметим лишь, что этот тренд отражается не только в появлении новых NoSql решений и развитии старых. Еще одна грань — смешение противоположностей, а именно поддержка хранения schema-less данных в традиционных реляционных базах. В этой серой области на стыке реляционной модели хранения данных и всего остального кроется головокружительное количество возможностей. Но, как и всегда, нужно уметь найти баланс, который подходит именно для ваших данных. Это бывает трудно, в первую очередь из-за того, что приходится сравнивать мало сравнимые вещи, например, производительность NoSql решения с традиционной базой данных. В этой небольшой заметке будет предложена такая попытка и приведено сравнение производительности работы с jsonb в PostgreSQL с json в Mysql и с bson в Mongodb.


Что, черт возьми, вообще происходит?


Краткие вести с полей:
  • PostgreSQL 9.4 — новый тип данных jsonb, поддержка которого будет немного расширена в грядущей PostgreSQL 9.5
  • Mysql 5.7.7 — новый тип данных json

и ряд других примеров, о которых расскажу в следующий раз. Замечательно то, что эти типы данных предполагают не текстовое, а бинарное хранение json, что делает работу с ним гораздо шустрее. Базовый функционал везде идентичен, т.к. это очевидные требования — создать, выбрать, обновить, удалить. Самое древнейшее, почти пещерное, желание человека в этой ситуации — провести ряд benchmark'ов. PostgreSQL & Mysql выбраны, т.к. реализация поддержки json очень похожа в обоих случаях (к тому же они находятся в одинаковой весовой категории), ну а Mongodb — как старожил NoSql мира. Работа, проведенная EnterpriseDB, немного в этом плане устарела, но ее можно взять, в качестве первого шага для дороги в тысячу ли. На данный момент целью данной дороги является не показать, кто быстрее/медленее в искусственных условиях, а постараться дать нейтральную оценку и получить feedback.

Исходные данные и некоторые детали


pg_nosql_benchmark от EnterpriseDB предполагает достаточно очевидный путь — сначала генерируется заданный объем данных разного вида с легкими флуктуациями, который затем записывается в исследуемую базу и по нему происходят выборки.
Функционал для работы с Mysql в нем отсутствует, поэтому его понадобилось реализовать на основе аналогичного для PostgreSQL. На данном этапе есть только одна тонкость, когда мы задумываемся об индексах — дело в том, что в Mysql не реализовано
индексирование json на прямую, поэтому приходится создавать виртуальные колонки и индексировать уже их. Помимо этого, меня смутило то, что для mongodb часть сгенерированных данных размером превышает 4096 байт и не вмещается в буфер mongo shell, т.е. просто отбрасывается. В качестве хака получилось выполнять insert'ы из js файла (который еще и приходится разбивать несколько chunk'ов, т.к. один не может быть больше 2GB). Помимо этого, чтобы избежать издержек, связанных со стартом шелла, аутентификацией и проч., совершается соответствующее количество «no-op» запросов, время которых затем исключается (хотя они, на самом деле, достаточно малы).

Со всеми полученными изменениями проверки проводились для следующих случаев:
  • PostgreSQL 9.5 beta1, gin
  • PostgreSQL 9.5 beta1, jsonb_path_ops
  • PostgreSQL 9.5 beta1, jsquery
  • Mysql 5.7.9
  • Mongodb 3.2.0 storage engine WiredTiger
  • Mongodb 3.2.0 storage engine MMAPv1

Каждый из них был развернут на отдельном m4.xlarge инстансе с ubuntu 14.04 x64 на борту с настройками по умолчанию, тесты проводились на количестве записей, равном 1000000. Для тестов с jsquery надо прочитать readme и не забыть установить bison, flex, libpq-dev и даже postgresql-server-dev-9.5. Результаты будут сохранены в json файл, который можно визуализировать с помощью matplotlib (см. здесь).

Помимо этого возникали сомнения относительно настроек, связанных с durability. Поэтому я провел пару дополнительных тестов для следующих случаев (на мой взгляд, часть из них является скорее теорией, т.к. вряди кто-то будет использовать такие настройки в живую):
  • Mongodb 3.2.0 journaled (writeConcern j:true)
  • Mongodb 3.2.0 fsync (transaction_sync=(enabled=true,method=fsync))
  • PostgreSQL 9.5 beta 1, no fsync (fsync=off)
  • Mysql 5.7.9, no fsync (innodb_flush_method=nosync)


Картинки


Все графики, связанные со временем выполнения запросов, представлены в секундах, связанные с размерами — в мегабайтах. Соответственно, для обоих случаев чем меньше величина, тем больше производительность.

Select


image

Insert


image

Insert (custom configuration)


image

Update


image
Еще одним изменением относительно оригинального кода pg_nosql_benchmark было добавление тестов на обновление. Здесь явным лидером оказалась Mongodb, скорее всего за счет того, что в PostgreSQL и Mysql обновление даже одного значения на данным момент означает перезапись всего поля.

Update (custom configuration)


image
Как можно предположить из документации и подсмотреть в этом ответе, writeConcern j:true — это наивысший возможный уровень durability для одного сервера mongodb, и судя по всему он должен быть эквивалентен конфигурациям с fsync. Я не проверял durability, однако интересно, что для mongodb операции обновления с fsync получились намного медленнее.

Table/index size


image
image

I have a bad feeling about this


Измерение производительности — слишком скользкая тема, тем более в этом случае. Все, что описано выше, нельзя считать полноценным и завершенным бенчмарком, это только первый шаг к тому, чтобы понять текущую ситуацию — что-то вроде пищи для размышлений. В данный момент мы занимаемся проведением тестов с использованием ycsb, а также, если повезет, сравним производительность кластерных конфигураций. Помимо этого, буду рад всем конструктивным предложениям, идеям и правкам (т.к. я вполне мог что-то упустить).

Комментарии (15)


  1. igor_suhorukov
    30.12.2015 14:38
    -4

    Отличную тему выбрали! Сравнение объектно-реляционных расширений классических СУБД и NoSQL MongoDB


  1. Bozaro
    30.12.2015 15:07
    +3

    Очень не хватает для полноты картины конфигурационных файлов (от них очень много зависит).

    Такой разлет в обновлении, скорее всего, вызван разными гарантиями транзакций (записаны ли данные физически на диск после COMMIT?).


    1. erthalion
      30.12.2015 15:53

      Специфических конфигураций ни для одной базы не было, все использовалось «из коробки».
      Насчет записи данных на диск в тестах для update — я пробовал выполнить этот тест с использованием опции write concern j:true, чтобы убедиться, что операция попала в журнал (насколько я понимаю, это рекомендуемый способ управления гарантиями транзакций в mongodb). Это, конечно, замедлило выполнение теста, но все равно обновление происходило гораздо быстрее, чем в PostgreSQL или Mysql (этих данных на графиках сейчас нет).


      1. Bozaro
        30.12.2015 16:00
        +4

        Получается странное сравнение:

        • MySQL и PostgreSQL выполняют fsync (данные выживут после внезапного перезапуска сервера)
        • MongoDB не выполняет fsync (данные выживут после перезапуска Mongo)

        По-хорошему надо либо отключить fsync у MySQL и PostgreSQL, либо включить у MongoDB.


        1. erthalion
          30.12.2015 16:09

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


        1. erthalion
          02.01.2016 09:29

          Добавил графики для тестов с разным уровнем durability.


  1. gnomeby
    30.12.2015 15:34

    У вас в Mongo операции с fsync проходили?


    1. erthalion
      30.12.2015 15:56
      -3

      Нет, единственное, что я пробовал изменить относительно дефолтного использования — это добавлял write concern j:true (данных с этой опцией на графиках сейчас нет)


      1. gnomeby
        30.12.2015 16:09
        +5

        тогда тесты не имеют смысла


        1. erthalion
          30.12.2015 16:30
          -1

          Замечу, что помимо обновления и вставки здесь фигурируют тесты на выборку + итоговые размеры, они, надеюсь, даже при отсутствии fsync имеют смысл.


  1. lega
    30.12.2015 16:09
    +3

    Судя по исходникам, замеряется не скорость запросов, а скорость запуска «шела» (mongo/psql/mysql) с авторизацией, и скорость их вывода результата.


    1. erthalion
      30.12.2015 16:24
      -1

      Да, это все в итоге включается во время исполнения запроса. В том числе поэтому данные тесты лишь грубая оценка производительности (хотя надо бы еще убедиться, что в других способах изменения нет подобных накладок).


      1. ivnik
        30.12.2015 16:27
        +5

        Это не грубая оценка, а полный треш.


      1. lega
        30.12.2015 16:37

        это все в итоге включается во время исполнения запроса
        Для интереса сейчас замерил на коллекции 100к, выполнение команды через монгошел (с запуском без авторизации) ~50мс, выполнение этой же команды из python ~0.4мс, победит тот у кого шел быстрее.

        Когда программисты будут запускать внешний бинарник для запросов к БД вместо использования встроенного драйвера, тогда данный бенчмарк можно будет и рассмотреть.


        1. erthalion
          02.01.2016 09:28

          Обновил пост и код бенчмарков, теперь эти задержки исключаются (но они получилось достаточно незначительными, на 2-3 порядка меньше, чем основые величины).