![image](https://habrastorage.org/getpro/habr/post_images/ac4/a74/a4a/ac4a74a4a4938a4b09fb295912b9ade1.jpg)
Привет, Хабр! Эта публикация — попытка развеять некоторые популярные мифы и легенды о MySQL. Я не ошибся с хабом, так как поводом для написания послужила публикация varanio Возможности PostgreSQL, которых нет в MySQL, и наоборот отсюда же. Сама публикация в части критики MySQL хоть и неидеальна, но вполне корректна, а вот комментарии к ней наводят на грустные размышления.
Вообще говоря, я собирался написать публикацию о возможностях MySQL, которые не реализованы или реализованы в PostgreSQL хуже. Но для того, чтобы не мешать много тем в одну публикацию, и учитывая довольно нелёгкую работу по сравнению того, что я знаю очень хорошо (MySQL) с тем, что я знаю очень плохо (PostgreSQL), такую публикацию я решил отложить на потом и для начала ответить сразу на многие комментарии из публикации varanio.
Зачем мне это нужно? Ну, во-первых, в Интернете опять кто-то не прав. Что сама по себе невелика проблема, но к сожалению многие из этих прохладных историй мне приходится встречать не только в Интернете, но и слышать на различных российских конференциях, в том числе на элитных конференциях от элитных докладчиков. Особенно странно слышать не вполне корректные утверждения про MySQL от представителей компании Postgres Professional, к которой я отношусь с большой симпатией и желаю ей всевозможных успехов и процветания. Поэтому в рамках культурного обмена и для повышения технического уровня дискуссии о достоинствах и недостатках PostgreSQL и MySQL, я бы для начала хотел не только разобрать типичные заблуждения, но и предложить правильные формулировки, а также обрисовать действительно серьёзные (на мой взгляд) проблемы в MySQL на текущий момент.
Итак, школа юного холиворщика. Урок первый: «Критикуем MySQL грамотно». Дзынь!
Как не нужно критиковать MySQL?
Начнём с самых простых мифов: околотехнических.
«MySQL — это legacy»
Я просто приведу краткий и далеко не полный список всем известных компаний, где MySQL играет ключевые роли в инфраструктуре и бизнесе: Github, Wikipedia, Google, Facebook, Twitter, LinkedIn, Alibaba, Taobao, Booking.com, AirBnB, Dropbox, Pinterest, GroupOn, Yelp.
Я знаю, что есть крупные и известные проекты на PostgreSQL. Но, во-первых, мало кто может сравниться по масштабам с указанными проектами. А во-вторых, этот список просто полезно вспоминать, чтобы не терять связь с реальностью, когда кто-то выдаёт что-то глубокомысленное в стиле «MySQL не нужен».
«У MySQL нет сообщества!»
О критериях оценки масштабов и активности сообщества можно спорить долго, но критерии в http://db-engines.com/en/ranking мне кажутся довольно разумными.
С другой стороны в России популярность PostgreSQL и активность сообщества действительно несколько выше «среднемировых» трендов. Не совсем понимаю, чем это вызвано, но не вижу в этом ничего плохого.
«У MySQL есть сообщество, но оно фрагментировано!»
Вообще никогда не мог понять, о чём здесь речь. Это звучит так, как будто пользователям Oracle MySQL нужно полностью переучиваться, чтобы работать с MariaDB. Или DBA, работающий с MariaDB абсолютно ничего не знает о Percona Server. В реальности 99% навыков, книг, статей, утилит, полученных где-то советов и пр. можно спокойно использовать на любом варианте MySQL.
«У MySQL много форков и у них разброд»
Впервые это странное утверждения я услышал несколько лет назад на одной из российских конференций. С тех пор я довольно часто слышу это от людей, явно плохо представляющих о чём они говорят. В том числе и в упомянутом посте varanio. Я никогда не мог получить внятных ответов на два простых вопроса:
- В чём заключается «разброд»?
- Сколько «форков» MySQL вы можете насчитать?
Не удалось получить внятных ответов и в этот раз, но я и уже не особо и надеялся. В развитии свободного и открытого ПО «форки» встречаются сплошь и рядом. При этом обычно считается, что это хорошо, для того оно свободное и открытое. Кроме того, «форков» и «бранчей» у MySQL даже меньше, чем во многих других известных проектах. Меньше, чем в самом PostgreSQL, раз уж на то пошло.
Кстати, этот аргумент поразительно похож на FUD от Microsoft. Вот так Microsoft рекламировали своё преимущество над Linux в начала этого века:
![Сейчас Microsoft выпускает свои дистрибутивы Linux](https://habrastorage.org/files/e7f/984/2f0/e7f9842f0e8642c49a33f7abac143d0c.jpg)
«MySQL принадлежит Oracle (а PostgreSQL никому)»
Это, конечно, верно, но какие важные выводы из этого можно сделать, я не очень понимаю, а полёт мысли критиков обычно на этом останавливается.
Чисто теоретически, Oracle вправе свернуть проект / закрыть исходники / сделать его платным. Насколько это реально — каждый может решать сам. По мне так абсолютно нереально — этого можно было опасаться в момент покупки Sun Microsystems Ораклом, но прошло уже 5 лет (Карл!) и за это время MySQL развивался быстрее, чем когда-либо до покупки. Кроме того, в Oracle сидят не дураки и там прекрасно понимают, что закрытие MySQL больше всего ударит по самому же Oracle.
«Субъективно в PostgreSQL меньше багов»
Может так оно и есть, но как бы вот сравнить объективно при отсутствии баг трекера в PostgreSQL?
На этом с нетехническими мифами заканчиваем и переходим к более содержательным.
«PostgreSQL в разы быстрее MySQL»
Я много лет занимаюсь оптимизацией кода MySQL и, как следствие, часто провожу нагрузочные тесты, правда в основном между разными версия и вариантами MySQL. Вот что я могу сказать по поводу таких утверждений: я абсолютно уверен, что есть нагрузки/запросы/конфигурации, где PostgreSQL будет работать в разы быстрее MySQL, так же, как я уверен и в обратном — существуют нагрузки/запросы/конфигурации, в которых MySQL окажется в разы быстрее PostgreSQL. В случае с MySQL есть ещё один важный критерий — используемый движок (storage engine), которые тоже может изменять ситуацию не только в разы, но и на порядки.
Каждый раз, когда кто-то жалуется на произодительность (неважно, MySQL или PostgreSQL), его просят показать схему/запросы/explain/конфигурацию. И это не случайно — все разговоры о производительности можно вести только в таком контексте. Общие утверждения не котируются.
«Репликация в PostgreSQL сделана по уму. А в MySQL нет»
В MySQL реализована логическая репликация (независимо от statement-based или row-based форматов). В PostgreSQL репликация физическая (не «бинарная», а именно физическая).
У каждого подхода есть свои плюсы и минусы. Даже не нужно быть спецом по той или иной СУБД, чтобы это понять. Пользователи MySQL накопили большой опыт обхода или сглаживания негативных эффектов от логической репликации, но скорее всего когда-нибудь появится и физическая, потому что вообще нужна и та и другая. Подозреваю, что в PostgreSQL идёт аналогичный процесс, но в обратную сторону.
Эта тема очень обширная и вызывает столько вопросов, что я подумываю об отдельном посте.
«MySQL нестрого работает с данными»
Варианты: «MySQL втихую делит на ноль!»
Технически корректный, но скучный вариант этого утверждения звучит так: «В MySQL версий < 5.7 нужно не забыть включить правильные режимы SQL»
В 5.7 настройки «строгости» довольно разумны по умолчанию. Причина, почему этого так долго не делали, банальна: как и для любого популярного проекта, главный конкурент MySQL — это его собственные старые версии. Приходится тянуть обратную совместимость со старыми криво написанными приложениями. В 5.7 с этим решили покончить, что хорошо.
«В MySQL есть нетранзакционные движки типа MyISAM»
Да, но тут не очень понятно, в чём критика. Говорить о MyISAM в 2015-м году можно только в плане поддержки legacy приложений. Честно говоря, последний раз живого пользователя MyISAM я встречал лет пять назад.
Хотя есть нетранзакционные движки, от которых и не требуется никакая транзакционность. Например CSV. Чтобы было проще понять пользователю PostgreSQL, движок CSV — это примерно как file_fdw. Оно скорее для обмена данными с другими приложениям, а не для собственно хранения данных.
«В MySQL транзакции прикручены как-то сбоку»
Под этим каждый понимает что-то своё. Кто-то так считает, потому что нельзя завернуть DDL в транзакцию. Что конечно правда, но проблема растёт не из «транзакций сбоку», а из кривых DDL (подробнее об этом ниже в разделе «Как нужно критиковать MySQL»).
Скучный, технически корректный вариант: «В MySQL нетранзакционные DDL».
Кто-то говорит, что ошибка внутри транзакции не приводит к её автоматическому откату. Да, ошибки бывают разные (например, lock wait timeout), и для них приложение имеет возможность повторить последний statement, а не откатывать всю транзакцию. Насколько я знаю, Oracle и SQL Server по умолчанию ведут себя так же — это не есть нарушение каких-либо стандартов. В PostgreSQL для возможности повторить последний statement при ошибке пришлось бы заворачивать каждый statement внутри транзакции в SAVEPOINT. Что, например, и привело к возникновению ON_ERROR_ROLLBACK для psql. Т.е. как обычно, подходы разные, со своими плюсами и минусами.
«В MySQL очень дорогие DDL»
Имеется в виду пересоздание таблицы при определённых операциях, например при удалении constraint. Уже в 5.6 можно делать ALTER без пересоздания таблицы почти для всех операций . В частности, constraint можно удалять без пересоздания. В 5.7 список in-place операций ещё шире.
Я не знаю, как с этим в PostgreSQL (быстрый поиск показывает, что определённые проблемы таки имеются). Но для MySQL есть утилита pt-online-schema-change, которая позволяет обходить многие ограничения ALTER TABLE в MySQL и про которую часто не знают или забывают.
«В MySQL какой-то неправильный MVCC»
Механизм MVCC в InnoDB реализован хорошо. С точки зрения пользователя, он практически идентичен реализации MVCC в Oracle, и сильно похож на реализацию в PostgreSQL. Но отличия в поведении многих удивляют, что порождает похожие мифы. Дело в том, что стандарт очень невнятно определяет многие тонкие моменты в разных уровнях изоляции. В результате каждая СУБД интерпретирует эти «пробелы» по-своему. Хорошее сравнение MVCC в Oracle, PostgreSQL и в MySQL/InnoDB можно почитать здесь. Но даже там отражены не все нюансы.
За последние 10 лет на оптимизацию и масштабируемость MVCC в InnoDB было потрачено огромное количество усилий. Я тоже к этому приложился.
Обновлено 02.04.2017: более формальная попытка разобраться в отличиях реализации MVCC в разных СУБД, включая PostgreSQL и MySQL.
«Из-за подключаемых движков MySQL записывает данные на диск по 2, 3, 4, а в военное время и по 5 раз»
Фантастически наивное утверждение. Логика простая:
- запись в файлы данных
- запись в транзакционный журнал
- запись в бинарный журнал
- да ещё там какой-то doublewrite buffer, который судя по названию ещё два раза пишет!
Здесь нужно поговорить о характеристике «write amplification». Я не смог подобрать хороший термин в русском языке, но по сути это соотношение общего объёма данных, записанных на диск, к общему объёму данных, переданных клиентом. Посмотрим из чего она складывается.
Во-первых, запись в файлы данных происходит только при сбросе обновлённых страниц на диск. Как долго страница может обновляться в памяти перед тем, как будет сброшена на диск (и соответственно, насколько сильно изменится write amplification) зависит от большого количества параметров: размера общего буфера (buffer pool в InnoDB), размера транзакционного журнала, алгоритма, управляющего сбросом страниц, настроек сервера и естественно типа нагрузки. Это всё применимо как к MySQL, так и PostgreSQL.
Во-вторых транзакционный журнал содержит не только обновления записей в таблице, но и все физические изменения в файлах данных. На примере InnoDB, это управление индексными деревьями (разделение страниц при заполнении, объединение при удалении, перестроение дерева и т.д.), удаление старых версий записей (операция purge), операции над change buffer и прочая внутренняя бухгалтерия. Это тоже применимо как к MySQL, так и PostgreSQL.
В-третьих, бинарный журнал содержит только логические изменения в данных: он ничего не «знает» про формат файлов данных и всю их внутреннюю бухгалтерию. Write amplification для бинарного журнала зависит от многих параметров (statement/row-based, binlog_row_image и прочие настройки). Кроме того бинарный лог можно отключать — он нужен часто, но не всегда.
Ну, и наконец doublewrite buffer задействуется тоже только при сбросе страницы на диск, а не при каждом INSERT/DELETE/UPDATE. Абсолютно аналогичный механизм, только в профиль, в PostgreSQL называется «full page write». И там и там его можно отключить при определённых условиях.
Нужно также учесть избыточность форматов данных (т.е. накладные расходы на служебную информацию), размер страницы (в InnoDB его можно указать при создании базы), компрессию и ещё много чего.
Надеюсь, из всего этого понятно, что рассчитать характеристику write amplification невозможно — слишком много параметров. Её можно измерить для конкретной нагрузки, конкретной конфигурации и (в случае MySQL) конкретного движка. Например, в случае оптимизированных для записи движков типа TokuDB или MyRocks эта характеристика будет сильно ниже, чем в InnoDB, потому что они для этого и создавались.
Всё, что можно сказать — это то, что binary log (когда включен) приводит к дополнительным накладным расходам на запись. Насколько большим расходам, и будет ли суммарный write amplification больше той же характеристики в PostgreSQL, нельзя сказать даже примерно без измерения на конкретных нагрузках и конфигурациях.
Такая вот скучная правда.
«В MySQL слишком много журналов»
Расписывать все типы журналов я не буду, их назначение всем более-менее понятно. Вопросы в основном вызывает binary log. Binary log — это «плата» за возможность иметь подключаемые движки. «Подключаемые» не в том смысле, что они могут подключатся динамически как плагины (раньше такой возможности не было), а в том смысле, что их вообще может быть несколько, и сервер работает с разными движками через API.
Поскольку физическое представление данных на диске может меняться в зависимости от используемых движков, а единое сериализованное представление всех изменений необходимо для репликации, такое представление записывается в абстрагированном от физических данных, логическом виде. Как я уже писал, логический журнал вовсе не обязательно дублирует записи в физические журналы при обновлении данных.
С логическим журналом, как и с логической репликацией, можно делать всякие интересные штуки. В частности, это активно используется в технологии Galera. Я собираюсь сделать краткий обзор всего этого хозяйства в следующем посте.
«В MySQL плохой консольный клиент»
Как я понял, под этим понимают отсутствие контекстно-зависимого автодополнения. Я согласен, это может быть удобно. Но для того, чтобы это почувствовать, нужно достаточно много времени проводить в консольном клиенте. Я как разработчик больше времени провожу в редакторе. Мне кажется, большая часть работы DBA тоже выполняется скриптами, а не вручную в консольном клиенте.
Но для тех, кому это очень нужно, недавно появился проект mycli с умным автодополнением и даже подсветкой синтаксиса. А для любителей красивых GUI есть MySQL Workbench, где есть по-моему вообще всё. Никогда не пользовался ни тем, ни другим.
Обновлено 05.10.2017: В бета-версии MySQL Shell, нового клиента командной строки от Oracle, появилась поддержка автодополнения для SQL.
В любом случае, правильнее было бы говорить «в консольном клиенте MySQL нет контекстного автодополнения», потому что вся критика похоже к этому и сводится.
Как можно было критиковать MySQL, но уже неактуально?
Отдельно хотелось бы выделить утверждения, которые в общем верны, но теряют актуальность в свете скорого релиза MySQL 5.7.
«В MySQL нет поддержки JSON»
Ограниченная поддержка была и раньше в JSON UDF от svetasmirnova. В 5.7 есть нативная поддержка. Насколько она сравнима с PostgreSQL, я сказать не могу, но в любом случае утверждение в таком виде уже устарело.
«В MySQL нет функциональных индексов»
В 5.7 функциональные индексы реализованы как индексируемые virtual/generated columns. В MariaDB они есть уже давно, но индексирование возможно только для материализованных виртуальных колонок.
Как нужно критиковать MySQL?
Переходим к, наверное, самой интересной части. Реальные и серьёзные проблемы действительно есть, но вот о них как раз в холиворах говорят очень редко. На самом деле, судя по блогам и докладам на конференциях, пользователей MySQL больше интересует вовсе не отсутствие, скажем, оконных функций, а вопросы горизонтального масштабирования, шардинга, кластеризации, high availability, облачных платформ и связанные с ними вопросы автоматизации, мониторинга, защиты информации и прочего. Как следствие, именно в этих областях сосредоточны усилия разработчиков.
Но, поскольку, мы здесь говорим о критике со стороны сообщества PostgreSQL, а вот этих всех вопросов она как правило и не затрагивает, здесь я перечислю справедливые, на мой взгляд, критические замечания из услышанных или прочитанных ранее, но сформулированные технически корректно.
Отсутствие транзакционного словаря данных
В MySQL «словарь данных» представляет из себя набор нетранзакционных файлов (.frm, .par и т.д.) Это тяжёлое наследие с самых давних времён и оно создаёт огромное количество проблем.
Это не только отсутствие транзакционных DDL, для которых словарь данных является необходимым условием. Это и проблемы с дорогостоящими запросами к INFORMATION_SCHEMA, проблемы с расширяемостью формата метаданных, проблемы с физическими бэкапами, которые вынуждены блокировать сервер по сути только для обеспечения согласованности транзакционных данных с нетранзакционным словарём, и прочие.
Работа над этим всем начата, но результатов в 5.7 мы уже точно не увидим.
Для Oracle ничего, кроме InnoDB не существует
К сожалению, Oracle не особо волнует судьба сторонних движков по понятным причинам. Я не думаю, что концепция «подключаемых» движков будет свёрнута в ближайшее время, но и заботиться о поддержке других движков они явно не собираются.
В результате многие функциональные возможности реализованы только в InnoDB. Примеры: foreign keys, full text search, spatial indexes, virtual columns. Вряд ли все эти функции мы увидим в скором времени в TokuDB или MyRocks. А если они и будут реализованы, они не будут совместимы по функционалу с тем, что в InnoDB.
Несовершенный оптимизатор запросов
Оптимизатор всегда был одним из слабых мест MySQL. Хотя я подозреваю, что если начать детальные распросы, то большинство критиков начнут рассказывать о проблемах, исправленных 5-10 лет назад. Полезные улучшения есть в каждой новой версии, но всё-таки прогресс медленее, чем многим хотелось бы. На то есть исторические причины, хотя дело здесь не столько в концепции подключаемых движков, сколько в очень несовершенной архитектуре оптимизатора. Она требует серьёзного пересмотра и рефакторинга, и работа в Oracle в этом направлении идёт, но такие вещи быстро сделать невозможно.
Слабая поддержка «продвинутого» SQL
Эта проблема отчасти связана с предыдущей. Но да, нет оконных функций, CTE и ещё много-много чего. Насколько это важно для пользователей MySQL — вопрос открытый. Как я уже писал, по блогам и докладам на конференциях особо не скажешь, что вот какой-то функциональности SQL ну очень не хватает. Судя по изменениям в релизах MySQL, для Oracle это тоже далеко не самое приоритетное направление.
![](https://habrastorage.org/webt/59/d5/ef/59d5ef820f58d935160952.png)
Обновлено 05.10.2017: Поддержка CTE и оконных функций появилась в MySQL 8.0. Интересно, что по результатам тестирования известного PostgreSQL-евангелиста Маркуса Винанда MySQL на данный момент имеет наиболее полную поддержку CTE среди всех популярных СУБД, включая PostgreSQL.
Слабая поддержка GIS
Тоже верная критика, но на мой взгляд в Oracle к этому относятся серьёзно. В 5.7 GIS был переписан с нуля, в InnoDB добавлены spatial indexes, используется Boost.Geometry вместо самописного кода и начата работа над соответствием стандартам. Подозреваю, что в следующих версиях будет много интересного.
Обновлено 05.10.2017:Поддержка GIS была сильно расширена в MySQL 8.0. Из ключевых моментов — поддержка не-декартовых SRS и расширенный набор функций для обработки пространственных данных. Подробности можно посмотреть в этой презентации. Также было бы интересно сравнить это с PostGIS. Судя по моему общению с разбирающимися в этой теме людьми, в некоторых моментах PostGIS уже уступает по возможностям встроенной поддержке GIS в MySQL 8.0.
Заключение
Наверняка я что-то упустил в каждом разделе, но уверен, что в комментариях дополнят. Одной публикацией всё охватить невозможно, но если кого-то эта публикация заставит вдумчиво и скептически относиться к критике MySQL на Хабре и конференциях, то я давил кнопки не зря. Написать отдельно о логической/физической репликации и функциях MySQL, которые не реализованы в PostgreSQL или реализованы хуже, мне было бы интересно, и я, наверное, рискну, если, конечно, меня не заминусуют окончательно.