В последнее время в web-программировании появился очередной тренд — Key-Value базы данных. Существует просто великое множество KV-решений, — одно лучше другого. Но так ли они важны и какая от них польза? Разрешите мне немного порассуждать о происхождении KV-хранилищ.

Нет дыма без огня Link to heading

Недовольство реляционными базами данных начало появляться давно. Оказалось что на некоторых use case’ах они не такие быстрые как хотелось бы. Они слишком сложные для того чтобы большинство программистов понимало как они работают, а следственно и то, как их использовать. Идея изоляции программиста от физических деталей хранения данных с треском провалилась. Если вы не знаете нюансов вашей РСУБД, то эффективно использовать ее на большом dataset’е или при высокой конкурентной нагрузке у вас не получится.

Помните чему вас учили в университете? Первая нормальная форма, вторая, тертья, Бойса-Кодда и т.д. Бурный рост интернет-аудитории с одной стороны и функциональности web-приложений с другой привел к тому, что пришлось пересмотреть паттерны использования реляционных СУБД. Сейчас, для того чтобы получить от реляционной БД приемлемый уровень пропускной способности, необходимо денормализовывать данные1, отказываться от распределенных транзакций и проверки целостности по внешним ключам2.

Но не стоит спихивать эти проблемы на не дальновидность или не профессионализм разработчиков реляционных СУБД. Насколько хорошо вы себе представляете как работает реляционная СУБД? Давайте проведем quick test. Ответьте на следующие вопросы:

  • вы знаете что такое план выполнения запроса и как БД его строит?
  • вы знаете как БД использует несколько индексов для фильтрации в случае, если нет index’а покрывающего все условия фильтрации?
  • вы знаете как БД использует индексы для сортировки, группировки и join’ов?
  • вы знаете сильные и слабые стороны hash и btree индексов?
  • вы знаете разницу между nested loop join, merge join и hash join?

Если вы утвердительно ответили хотя бы на половину вопросов, то вы знаете, что какая бы продвинутая БД у вас не была, сложные join’ы, сложные критерии фильтрации и группировки требуют много времени, какие бы индексы вы ни создавали. Сделать эффективный индекс (индекс позволяющий не делать лишней работы и определить результат только по этому индексу) можно только если у вас невысокая вариативность выборок и вы знаете все виды возможных запросов.

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

KV-storage Link to heading

Однажды приведя свою систему к тому состоянию, когда большинство выборок в ней производится по первичному ключу, вы можете задаться вопросом: а на кой черт мне здесь реляционная СУБД? Действительно, все эти статистические выкладки по cardinality индексов, эвристики заменяющие множество lookup’ов на один sequential read нужны были только тогда когда мы не знали какой запрос прийдет от клиента. Теперь мы знаем — это lookup по id. А раз мы знаем, то мы можем написать хранилище не делающее ничего лишнего — KV-storage.

Вот так они и появились. KV-хранилища — это не “серебрянная пуля” и не “RDBMS killer”. Это следствие эволюции взглядов на паттерны доступа к данным. KV-хранилища быстрые лишь потому, что они предоставляют только один способ доступа к данным — lookup по id. Они быстрые потому, что не обременены, как РСУБД, необходимостью тратить лишнее время на определение оптимального плана выполнения запроса, чтобы сэкономить гораздо больше времени во время выполнения этого запроса.

Фронт NoSQL Link to heading

Но современные постреляционные базы данных (если позволите так их назвать) ушли гораздо дальше чем просто lookup по id. Сейчас начинают набирать популярность документо-ориентированные базы данных (CouchDB, MongoDB), которые предоставляют более сложные способы извлечения и модификации данных. Некоторые KV-хранилища умеют нативно работать с коллекциями. Но эти возможности все равно меньше чем у реляционных баз данных.

Весь фронт NoSQL держится на том, что большинству web-приложений не нужны все возможности реляционной БД. Ну или скажем так, они могут обойтись без всех возможностей реляционной БД в угоду производительности.

Масштабируемость KV-решений Link to heading

Очень распространенный стереотип заключается в том, что KV-хранилища очень легко масштабируются. По отношению к некоторым продуктам это утверждение есть ни что иное как подмена понятий.

Задумайтесь, что дает вам, например, memcachedb для того чтобы легко масштабироваться? Кто-то из вас может сказать: “Легко. Берем остаток от деления хеша первичного ключа на количество серверов и…”. Ну ладно ладно, я понял. Кто-то может вспомнить про consistent hashing. Отлично. Но дело в том, что это не хранилище дает вам эту возможность partitioning’а, а тот паттерн доступа к данным которым вы пользуетесь. С таким же успехом, я могу легко вместо memcachedb использовать MySQL и утверждать что “MySQL легко масштабируется”.

Справедливости ради, надо сказать что некоторые решения (Cassandra, Project Voldemort, Scalaris) сами по себе предоставляют решения для автоматического partitioning’а ключей по нодам кластера. В отношении этих решений утверждение о масштабируемости все же верно.

Выводы Link to heading

Суровая реальность научила нас тому, что для того чтобы быстро получать доступ к данным их необходимо хранить в удобном для оперирования над ними виде. Данные и их структура первичны в случае если вы хотите получить максимум производительности. Этому существует немало подтверждений 3 4 (впрочем, пока эти подтверждения находятся в областях не связанных напрямую с web-программированием, поэтому они могут показаться вам безосновательными).

Мой совет web-программистам заключается в том, чтобы они пересматривали паттерны доступа к данным в своих приложениях, и по возможности сводили их к более простым. Это может вам обеспечить необходимый уровень производительности, а позже и масштабируемости.

К KV-хранилищам я бы советовал относится более осторожно. 100K запросов в секунду выглядит конечно заманчиво, но помните, — любая реляционка на подобных запросах ведет себя довольно шустро. Внедрение же еще одного продукта в проект увеличивает его себестоимость владения.