Сравнение производительности MongoDB vs MySQL

Материал из Национальной библиотеки им. Н. Э. Баумана
Последнее изменение этой страницы: 23:54, 25 июня 2018.

Если посмотреть DB-Engines Ranking, то можно увидеть, что в течение многих лет популярность open source баз данных растет, а коммерческих — постепенно снижается.[1] Для многих приложений используют несколько различных баз данных для того, чтобы задействовать их сильные стороны. Ни одна база данных не оптимизирована для всех возможных потребностей. С одной стороны, это хороший выбор, с другой — нужно пытаться найти баланс, так как чем больше разных технологий задействовано в компании, тем сложнее их поддерживать, особенно если компания не очень большая.

Для того, чтобы сузить круг используемых баз данных, можно определиться с основным операционным хранилищем и использовать вместе с ним какие-то дополнительные сервисы. Например, для кэширования или полнотекстового поиска. При выборе основного операционного хранилища возникает два основных варианта: с одной стороны — проверенные временем реляционные СУБД, с другой — различные нереляционные подходы к хранению данных.

Эти рассуждения и подводят нас к сравнению именно MySQL и MongoDB, которые, согласно DB-Engines Ranking, являются самыми популярными представителями реляционных и нереляционных баз данных соответственно. К тому же компания MongoDB изначально очень активно фокусировалась на пользователях MySQL, поэтому очень часто у людей есть опыт использования и выбор между этими двумя технологиями.

В данной статье мы сравним производительность этих двух баз данных.

Сравнение основных возможностей

MongoDB (от англ. humongous — огромный) — документоориентированная система управления базами данных (СУБД) с открытым исходным кодом, не требующая описания схемы таблиц. Классифицирована как NoSQL, использует JSON-подобные документы и схему базы данных.[2]

MySQL — свободная реляционная система управления базами данных. Разработку и поддержку MySQL осуществляет корпорация Oracle, получившая права на торговую марку вместе с поглощённой Sun Microsystems.[3]

Ниже представлена сравнительная таблица двух баз данных.

Сравнение MySQL и MongoDB
Параметры MySQL MongoDB
Краткое описание Широко используемая свободная реляционная система управления базами данных Одно из наиболее популярных документных хранилищ
Основная модель хранения данных Реляционная база данных Документно-ориентированная NoSQL база данных
Дополнительная модель хранения данных База данных типа Key/Value, документно-ориентированная база данных База данных типа Key/Value
Вебсайт www.mysql.com www.mongodb.com
Документация dev.mysql.com/doc/ docs.mongodb.com/­manual
Разработчик Oracle MongoDB, Inc
Дата релиза 1995 2009
Текущая версия 8.0.11, Май 2018 3.6.5, Май 2018
Лицензия Открытое программное обеспечение, доступны коммерческие лицензии Открытое программное обеспечение, доступны коммерческие лицензии
Облачное Нет Нет
Язык реализации С и С++ С++
Поддерживаемые операционные системы сервера Linux, OS X, Solaris, Windows, FreeBSD Linux, OS X, Solaris, Windows
Схема данных да свободная
Типизация Да Да
Поддержка XML Да Нет
Поддержка вторичных индексов Да Да
SQL Да Нет
API и другие методы доступа Проприентарное нативное API, ADO.NET, JDBC, ODBC Проприетарный протокол с использованием JSON
Поддерживаемые языки программирования Ada, C, C#, С++, D, Delphi, Eiffel, Erlang, Haskell, Java, JavaScript (Node.js), Objective-C, OCaml, Perl, PHP, Python, Ruby, Scheme, Tcl Actionscript, C, C#, С++, Clojure, ColdFusion, D, Dart, Delphi, Erlang, Go, Groovy, Haskell, Java, JavaScript, Lisp, Lua, MatLab, Perl, PHP, PowerShell, Prolog, Python, R , Ruby, Scala, Smalltalk
Язык написания скриптов на стороне сервера Да JavaScript
Триггеры Да Нет
Методы разбиения Горизонтальное разбиение, шардинг с MySQL Cluster или MySQL Fabric Шардинг
Методы репликации Master-Master, Master-Slave Master-Slave
MapReduce Нет Да
Концепции согласования Немедленное согласование Согласованность в конечном счёте, Немедленное согласование
Внешние ключи Да Нет
Концепции транзакций ACID Нет
Параллелизм Да Да
Возможность хранения только в памяти Да Да
Контроль доступа пользователей Нет групп или ролей Назначение прав для отдельных пользователей и ролей

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

Терминология MySQL и MongoDB
MySQL MongoDB
Таблица Коллекция
Строка Документ
Столбец Поле
Вторичный индекс Вторичный индекс
JOIN-ы Вложенные документы, $lookup & $graphLookup
GROUP_BY Aggregation Pipeline

В дальнейшем мы также будем держать в уме эти соответствия, дабы не проговаривать одно и то же в терминах двух СУБД.

Критерии сравнения

Мы будем сравнивать скорость добавления, поиска, изменения и удаления записей из одной таблицы. Предварительно можно предположить, что результаты будут сопоставимы, так как MongoDB и разрабатывалась для работы с разнородными документами без связей между ними.

MySQL же, как одна из основных представителей реляционных баз данных, должна себя проявить при выборке данных, находящихся в некоторых отношениях друг с другом. Чтобы это продемонстрировать, мы создадим две таблицы и будем делать LEFT JOIN по одному из атрибутов этих двух таблиц. В MongoDB для выполнения той же операции мы будем использовать $lookup, являющийся аналогом LEFT JOIN для MySQL.

Выбор структуры таблиц и обоснование

Условно структуру таблиц запишем следующим образом: Таблица 1 (text_foreignKey):

{ 
    text: 'fifteen symbols',
    foreignKey: 'random(1,100000')
}

Таблица 2 (mainKey_value):

{ 
    mainKey: 1...100000,
    value: 1...100000
}

Первая таблица будет состоять из 30000 записей с одинаковым текстом и случайным внешним ключом в диапазоне от 1 до 100000. Вторая таблица будет состоять из 100000 записей, поля mainKey и value каждой строки будут равны между собой и принимать значения от 1 до 100000.

Текст в первой таблице нужен исключительно для увеличения объема записываемых данных. Атрибут mainKey второй таблицы имеет такое название, чтобы подчеркнуть, что он не является первичным ключом. Не сделали мы его первичным ключом из тех соображений, что это замедлит скорость выполнения операций в MySQL из-за дополнительной проверки целостности. Поля mainKey и value принимают одинаковые значения лишь для упрощения восприятия, концептуально это ни на что не влияет.

Следует отметить, что таблица 2 у нас больше таблицы 1. Сделали мы ее такой из нескольких соображений. Во-первых, это должно сделать поиск по mainKey немного сложнее. Во-вторых, вследствие первого факта, это позволит несколько усложнить процедуру join-а двух таблиц.

Описание тестов

Мы проведем следующие тесты:

  1. Добавление до 100000 строк в таблицу mainKey_value. Результаты добавления строк в таблицу text_foreignKey мы приводить не будем в силу их аналогичности.
    1. Код для MySQL:
      1     for i in range(100000):
      2         cursor.execute("insert into mainKey_value values(%d, %d)" % (i, i))
      
    2. Код для MongoDB:
      1     for i in range(100000):
      2         db.mainKey_value.insert({'mainKey': i, 'value': i})
      
  2. Соединение двух таблиц. В MySQL мы будем использовать LEFT JOIN, В MongoDB - $lookup. Здесь мы будем варьировать размер таблицы text_foreignKey от 10000 строк до 30000.
    1. Код для MySQL:
      1     for i in range(30000):
      2         cursor.execute("insert into text_foreignKey values('fifteen symbols', %d)" % generatedKeys[i])
      3         if not (i + 1) % 10000:
      4             cursor.execute("select * from text_foreignKey left join mainKey_value on text_foreignKey.foreignKey = mainKey_value.mainKey")
      5             selectedList = list(cursor)
      
    2. Код для MongoDB:
       1     for i in range(30000):
       2         db.text_foreignKey.insert({'text': 'fifteen symbols', 'foreignKey': generatedKeys[i]})
       3         if not (i + 1) % 10000:
       4             aggregated = db.text_foreignKey.aggregate([
       5                 {
       6                     "$lookup":
       7                         {
       8                             "from": "mainKey_value",
       9                             "localField": "foreignKey",
      10                             "foreignField": "mainKey",
      11                             "as": "foreignKeyDoc"
      12                         }
      13                 }
      14             ])
      15             aggregated_list = list(aggregated) # aggregated - это cursor, а нам нужно считать сами данные
      
  3. Поиск до 10000 строк в таблице mainKey_value по ключу mainKey.
    1. Код для MySQL:
      1     for i in range(rowRange):
      2         cursor.execute("select * from mainKey_value where mainKey = %d" % generatedKeys[i])
      3         foundRows = list(cursor)
      
    2. Код для MongoDB:
      1     for i in range(docRange):
      2         foundKeys = list(db.mainKey_value.find({'mainKey': generatedKeys[i]}))
      
  4. Изменение до 10000 строк в таблице mainKey_value по ключу mainKey.
    1. Код для MySQL:
      1     for i in range(rowRange):
      2         cursor.execute("update mainKey_value set value = %d where mainKey = %d;" % (random.randint(1, 100000), generatedKeys[i]))
      
    2. Код для MongoDB:
      1     for i in range(docRange):
      2         db.mainKey_value.update_many({'mainKey':generatedKeys[i]}, {'$set':{'value':random.randint(1, 100000)}})
      
  5. Удаление до 10000 в таблице mainKey_value по ключу mainKey.
    1. Код для MySQL:
      1     for i in range(rowRange):
      2         cursor.execute("delete from mainKey_value where mainKey = %d" % generatedKeys[i])
      
    2. Код для MongoDB:
      1     for i in range(docRange):
      2         db.mainKey_value.delete_many({'mainKey':generatedKeys[i]})
      

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

Полный исходный код тестов можно найти по ссылке на github.

Установка СУБД и ПО для тестирования

Инструкции по установке СУБД MySQL и MongoDB на OS Ubuntu можно найти здесь и здесь.

Для автоматизации тестирования использовались пакеты Python для двух СУБД. Инструкции по установке тут и тут.

Результаты

С индексацией

Insert index 1.png

MySQL примерно на 63 процента быстрее.

Search index 1.png

MySQL быстрее на 95 процентов.

Update index 1.png

А здесь разрыв меньше - около 47 процентов.

Delete index 1.png

Тут MySQL быстрее примерно на 53 процента.

Left join index 1.png

MySQL предсказуемо побеждает с большим отрывом — примерно в 12 раз.

Без индексации

Insert no index 1.png

MySQL быстрее примерно на 75 процентов.

Search no index 1.png

Здесь обе базы данных демонстрируют удивительное сходство.

Update no index 1.png

Первая победа MongoDB — она оказалась быстрее на 21 процент.

Delete no index 1.png

И снова победа MongoDB, разница 24 процента.

Left join no index 1.png

В данном тесте MySQL по-прежнему имеет большое преимущество, что и не удивительно. Хотя оно сократилось — теперь MySQL быстрее в 4.24 раза.

Демонстрация работы

Заключение

Полученные результаты тестов вполне соответствуют нашим ожиданиям. В тестах, где выполнялись простые запросы данных из одной таблицы, мы имеем более-менее схожие показатели: MySQL оказалась быстрее при работе с таблицей, имеющей индекс, в то время как MongoDB вышла победителем, когда индексы не использовались. При этом разрыв между двумя СУБД был не более чем в два раза, а не на порядок, как в случае с LEFT JOIN. Конечно, больший практический интерес представляет работа с проиндексированной таблицей, но даже в этом случае однозначного победителя выявить нельзя, потому что MongoDB подкупает своей гибкостью в создании документов, не привязанных к конкретной схеме.

Если же рассматривать работу с данными, имеющими некоторые зависимости друг от друга, то тут победитель очевиден - это MySQL, сюрпризов не произошло.

Стоит отметить, что при добавлении данных MySQL загружал CPU в среднем на 80%, в то время как MongoDB использовал лишь 45% возможностей процессора, так что в этом тесте победитель не очевиден. Во всех остальных тестах обе СУБД использовали 45-50% процессорного времени.

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

Источники

  1. DB-Engines[Электронный ресурс]: DB-Engines Ranking / Дата обращения: 21.06.2018. — Режим доступа: https://db-engines.com/en/ranking.
  2. Википедия [Электронный ресурс]: MongoDB / Дата обращения: 21.06.2018. — Режим доступа: https://ru.wikipedia.org/wiki/MongoDB.
  3. Википедия [Электронный ресурс]: MySQL / Дата обращения: 21.06.2018. — Режим доступа: https://ru.wikipedia.org/wiki/MySQL.

Внешние ссылки