Почему эти ваши модные NoSQL решения не так уж хороши
9 марта 2016
Многие скажут, что сегодня я выступаю в роли Капитана Очевидности, и будут совершенно правы. Тем не менее, в разных мейлинг листах и чятиках то и дело появляются люди с вопросами типа «посоветуйте мне идеальную базу данных, а то у меня SQL тормозит и вообще все говорят, что MongoDB сейчас в моде». C 2007-го года я успел поработать со многими СУБД, не исключая ряда NoSQL решений, и по данному вопросу я имею сказать следующее.
Сразу отмечу несколько моментов:
- Дальше речь идет про базы данных, но те же соображения применимы, например, и в вопросе «простой и понятный Sphinx против типа распределенного из коробки ElasticSearch»;
- Redis и Memcached я отношу к распределенным кэшам, а не базам данных — в редком проекте вы сможете использовать один только Redis;
Также, забегая немного вперед, отмечу, что почти все NoSQL решения прекрасно работают, если у вас (а) данные никогда не изменяются или (б) данные можно пересчитать или восстановить из иного источника, и потому их не страшно ненадолго потерять. Например, если вы храните в NoSQL какую-нибудь аналитику или там счетчики числа пользователей онлайн и коды капч, это будет прекрасно работать. Проблемы начинаются, когда вы пытаетесь использовать одно из NoSQL решений в качестве основной базы данных. И вот почему.
Пробовали Cassandra. Большая проблема с Cassandra заключается в том, что она разрешает конфликты при помощи таймстемпов, хранимых вместе со значениями в столбцах по принципу last write wins. Также Cassandra не поддерживает транзакций (так называемые «микротранзакции» — это всего лишь CAS). Для аналитики все это может быть ОК. При использовании Cassandra в качестве основной СУБД ваши данные очень быстро испортятся, если только вы не написали поверх CAS собственные транзакции, что медленно и на что все поголовно забивают, мол и так прокатит.
Еще большая проблема в Cassandra, что она написана на Java. В комментариях, конечно, скажут, что Java не тормозит. Проблема в том, что это не так. Вот РСУБД иногда ругают, что там нужно прочитать 5 страниц документации и подтюнить 10 параметров, чтобы все хорошо работало. Используя Cassandra с настройками по умолчанию, вы быстро столкнетесь с тем, что база данных иногда отвечает за 1 мс, а иногда — за 200 мс из-за stop the world. Эта проблема решаемая, но путем тюнинга уже около сотни параметров JVM, которые буквально никем не документированы и которые нужно грепать по исходникам этой самой JVM. Та же проблема характерна и для многих других NoSQL решений, написанных на высокоуровневых языках. Внезапно 10 прекрасно документированных параметров в мире РСУБД стали казаться не такой уж большой проблемой, правда?
Наконец, Cassandra использует LSM-tree. Этот способ хранения данных подходит далеко не под все нагрузки. Если вы пишите и удаляете много данных (например, решили использовать Cassandra для хранения очередей), это будет работать очень и очень плохо.
Пробовали Couchbase. В отличие от Cassandra, Couchbase не умеет даже ренджсканы, куда уж там до транзакций. И в отличие от Redis, Couchbase не умеет работать со значениями, как со словарями, массивами или какими-то иными контейнерами. На практике это жутко неудобно, особенно учитывая, что максимальная длина значения в Couchbase ограничивается 20 Мб. То есть, даже просто положить в Couchbase какой-нибудь список контактов с эффективным добавлением и удалением контактов, и так, чтобы они еще и не разъезжались, представляет собой большую проблему. Что еще представляет собой проблему, это автофейловер. Я столкнулся с неприятной ситуацией, когда он успешно отработал, но заново собрать кластер после восстановления упавшего узла никак не удавалось. При этом помочь с проблемой разработчики не особо спешили ни в мейлинг листе, ни в IRC. А починить все самому не представлялось возможным, так как что, где и как хранится — неясно. Как по мне, уж лучше шардировать данные вручную, точно зная, что, где и как лежит, чем использовать такие готовые решения.
Пробовали MongoDB. К счастью, с этой СУБД мне пришлось работать совсем недолго. MongoDB не выдерживает никакой критики, и ее недостатки, похоже, поняли уже все. Вручную шардированный PostgreSQL, который, напомню, уже давно умеет формат JSONB и автофейловер (см Stolon), будет куда более удачным выбором, чем MongoDB. Подробности смотри у Афира и еще, например, вот тут. Выбрав PostgreSQL, вы получите все тоже самое, только куда более стабильное, не теряющее данные, а также с нормальными локами, вьюхами, транзакциями, джоинами и вот этим всем.
Riak почти не пробовали, но много общались с теми, кто пробовал. Решение, видимо, довольно неплохое, при условии, что вас не смущает отсутствие транзакций и вьюх, мешанина из Erlang, Java и С в одном проекте (про Java см выше), необходимость платить деньги, если вы хотите репликацию между ДЦ, а также необходимость писать свою логику для разрешения конфликтов. Последнее представляет большую проблему для типичных веб-программистов, потому что на практике они скорее всего скажут «а чего париться, last write wins и все дела». Также беспокоит, что не так давно в компании-разработчике Basho все из-за чего-то переругались, и программистов, которые реально знали, как работает Riak, в ней, похоже, уже не осталось. Наконец, в качестве бэкенда для Riak обычно используют LevelDB, которому присущи все те же проблемы, что и в случае с Cassandra.
Также немного работали с Tarantool. Насколько я помню, Tarantool был довольно неплох. Думается, потому что в нем как раз нет никакого авторешардинга и автофейловера. В первом приближении, это облегченная РСУБД, целиком живущая в памяти, а также ведущая WAL и периодически делающая снапшоты. Пожалуй, главная проблема с Tarantool заключается в том, что он сравнительно мало распространен, и что вся разработка ведется в Mail.ru для своих задач. Также, будучи in-memory базой данных, Tarantool ближе к решениям вроде Redis, чем к полноценным СУБД. Помимо Tarantool вас также может заинтересовать его форки, например, Octopus.
Еще пара слов про некоторые другие СУБД. Была такая многообещающая СУБД под название FoundationDB. Потом ее купила Apple и СУБД не стало. Завтра точно так же может не стать какого-нибудь еще NoSQL решения. Выглядят многообещающими CockroachDB, RethinkDB и ScyllaDB. Все эти СУБД имеют те или иные недостатки из названных выше. К тому же, на момент написания этих строк, CockroachDB и ScyllaDB являются слишком сырыми, чтобы тащить их в продакшен. RethinkDB выглядит довольно стабильно, но там еще есть своя заморочка с драйверами — нормально использовать эту СУБД можно далеко не из всех языков программирования.
Резюмируя вышесказанное:
- Иногда говорят глупости вроде «а нам в проекте не нужны транзакции». Если вы не хотите чинить прод в ночь с субботы на воскресенье, вам нужна консистентность данных. А ее очень сложно получить, не имея транзакций. Подумайте, к примеру, о случае, когда вам нужно поменять значения по двум ключам, и приложение падает после изменения первого;
- Люди, которым «не нужны транзакции», видимо, не знают про уровни изоляции в РСУБД. Вы можете не только не использовать транзакции, но и проставить заодно уровень read uncommited, эффективно отказавшись от MVCC. Более того, по умолчанию редкая РСУБД использует по умолчанию (если вообще предоставляет) честный уровень serializable. В PostgreSQL, например, по умолчанию используется read commited, при котором два последовательных SELECT запроса могут увидеть разные данные. Получается забавно — люди хотят отказаться от транзакций, которые они вообще-то и так могут не использовать, и которые они, скорее всего, на самом деле и не использовали вовсе;
- Без нормальных ренджсканов, вьюх, джоинов, данных неограниченного размера и так далее жизнь тоже очень печальна. NoSQL решения попросту убоги по функционалу;
- NoSQL решения недостаточно гибкие. Способы хранения и шардирования данных, фейловера, репликации и так далее почти всегда жестко зашиты в коде и поменять их не представляется возможным. Что намного хуже, способы эти часто документированы либо плохо, либо совсем никак, и потому в случае возникновения проблем самому что-то починить оказывается невозможным;
- С NoSQL связаны высокие риски. Проблемы с драйверами, сырость кода, недостающие инструменты (различные прокси, инструменты резервного копирования и тд), и вообще выбранной вами СУБД завтра может попросту не стать. В мире NoSQL завести совершенно новый API или переписать дисковый бэкенд с нуля — вполне обычное дело, посмотрите на историю развития тех же Riak с Cassandra. См также Десять веских причин не тащить в продакшн новые игрушки;
- Иногда говорят «мы можем терять данные, и нам важна скорость и масштабируемость». Если вы можете терять данные, почему бы тогда не писать их сразу в /dev/null? Это работает очень быстро и просто прекрасно масштабируется;
- Использование Java, Go и прочих высокоуровневых языков, на которых так любят писать NoSQL, к сожалению, приводит к непредсказуемому времени ответа СУБД, и обходится эта проблема весьма нетривиально. Подумайте также про JIT с сопутствующими накладными расходами и вот это все. NoSQL решения весьма прожорливы в плане ресурсов. (Прежде, чем приплетать к этой проблеме autovacuum в мире РСУБД, примите во внимание, что vacuum можно делать по расписанию, когда нагрузка на базу минимальна. Все это, конечно, только если он вам действительно чем-то мешает);
- Никакая СУБД никогда волшебным образом не заработает хорошо из коробки (особенно в виртуалках AWS, особенно в Docker-контейнере, но это уже другая история). Да, ребята, программирование — это сложно, тут иногда приходится думать, иногда даже головой. Есть великое множество способов сделать репликацию, шардинг и автофейловер, ни один из которых не подходит всем и всегда, десятки параметров железа, которые приходится учитывать, и так далее;
- Заметьте также, что NoSQL решения, вообще-то говоря, имеют высокий порог вхождения. Работать с РСУБД умеют вообще все, кто когда-либо что-то делал для веба. Как с ними работать из любимого языка программирования, как проектируется схема БД, как производить миграции, как делать бекапы и восстанавливаться из них, как все это правильно мониторить и так далее — это всем давно известно. А вот найти человека, реально умеющего работать с какой-нибудь Cassandra, не так-то и просто;
- Забыл сказать, что в мире NoSQL вас ждет огромное количество маркетингового булшита с подогнанными невоспроизводимыми бенчмарками, пятью «новостями» за неделю в казалось бы технических блогах типа «еще 15 причин использовать для всего наш MapReduce в Docker-контейнере», при условии, что MapReduce и другой притянутый за уши функционал не работает нормально ни в одном из названных выше NoSQL решений, и так далее. Вам вообще хочется в этом всем вариться?
Тогда что же выбрать в качестве основной СУБД для нового проекта? Совет стандартный — возьмите любую РСУБД по вкусу. Если конкретнее, то я бы рекомендовал PostgreSQL, но в крайнем случае и какая-нибудь MariaDB, пожалуй, будет как минимум не хуже Монги. Затем масштабируйтесь вертикально до тех пор, пока это позволяет кошелек. Может так получиться, что для всего проекта вам будет достаточно одного сервера, плюс еще одной реплики для фейловера. Если это ваш случай, то вам крупно повезло, поздравляю! Иначе все тоже не так уж плохо, так как обычно 90% операций идут только на чтение, и их можно спокойно распределить по множеству реплик.
Вот если вы скоро упретесь на запись, только тогда следует подумать о разбиении базы данных на несколько, возможно, даже с шардингом. Хотя лучше обойтись без него, просто имея несколько совершенно независимых баз. К этому моменту вы уже будете хорошо понимать, что и куда можно перенести или как лучше пошардить, чего нельзя сказать о ранних этапах развития проекта. Также вы будете понимать, какие данные и в какое NoSQL решение можно перенести, если, конечно, в этом вообще есть смысл.
Помните, что Яндекс, Mail.ru, Alpari и великое множество других компаний успешно используют для своих задач РСУБД, в том числе с шардированием и фейловером. Не потому что в этих компаниях работают такие динозавры, а потому что для хранения основных данных ничего лучше РСУБД пока не придумали.
Метки: Разработка, СУБД.
Вы можете прислать свой комментарий мне на почту, или воспользоваться комментариями в Telegram-группе.