Команда VK Cloud перевела статью о базах «ключ-значение». Вы узнаете, в чем их преимущества перед другими БД, какие базы работают по этому принципу и чем они отличаются между собой.
В чем суть баз «ключ-значение»
Суть проста — объекты в них хранятся и извлекаются с помощью ключа. Так мы прощаемся с:
- таблицами, столбцами и вводом ant data — всем, что можно так или иначе назвать blob-объектом;
- отношениями между объектами;
- сложными операциями.
Что же мы получаем взамен, когда отбрасываем все это?
Скорость
В реляционных базах данных индексы реализованы посредством структуры B-Tree, которая выглядит следующим образом:
Это одна из фундаментальных структур данных в современном IT. Она выдающаяся, но имеет чисто математическую проблему: стоимость поиска у нее равна O(log(n/m)), где n — общее количество элементов, а m — количество элементов в одном узле. То есть стоимость поиска имеет тенденцию расти.
В отличие от этой структуры, большинство баз данных «ключ-значение» хранят данные в памяти и могут определять позицию элемента в массиве с помощью хеш-функции. Ее стоимость O(1), дешевле не бывает.
Горизонтальное масштабирование
Большинство БД «ключ-значение» работают по следующим правилам:
- Идентифицировать элемент можно только по ключу.
- Хеш-функция детерминированно преобразует ключ в целое число.
- Мы не выполняем никаких агрегатных операций либо ограничиваем их объем.
- При обновлении изменяется значение целиком, поскольку базе данных неизвестна схема хранения элементов. Хотя есть некоторые базы, которые обходят это правило.
При соблюдении таких правил нетрудно горизонтально масштабировать. В самом упрощенном виде:
hash(key) % NUMBER_OF_SERVERS
Хотя это чрезмерное упрощение, некоторые базы данных «ключ-значение» его используют.
Чего не следует ожидать от баз данных «ключ-значение»
Если вы жили в королевстве RDMS, то не все, к чему вы привыкли, стоит искать в базах данных «ключ-значение».
Транзакции
Вместо них у нас есть атомарность. А в чем разница?
- При атомарности операция будет выполнена или не выполнена. В случае сбоя данные не будут повреждены или частично изменены.
- При транзакционности появляется последовательность нескольких операций, которые выглядят как одна операция.
Плохо ли, что транзакций нет? Давайте посмотрим, нужны ли они вообще:
-
SELECT
— транзакции не нужны. Мы запрашиваем элемент и получаем его.
-
DELETE
— единственная команда DELETE атомарна, так что здесь проблем не возникает. Но транзакции могут понадобиться при множественных удалениях. Здесь все сводится к тому, как мы получили ключи элементов, которые нужно удалить. Если мы хранили их как значение другого ключа, переходим к complex statements flows. Если нам нужно удалить значения, ключи которых выполняют некоторый паттерн, просто повторите попытку удаления.
-
UPDATE
— здесь дела обстоят так же, как с DELETE.
-
Complex statements flows
— эта команда нужна, когда значение одного ключа содержит ключи, которые, например, нужно удалить. Это значит, что в базе данных появилось отношение, то есть мы пытаемся имитировать RDMS поверх базы данных «ключ-значение». Не стоит это делать.
Примеры
Хотя это очень простые базы данных, у них есть заметные отличия. Чтобы показать это, давайте изучим три самые популярные базы данных «ключ-значение» по мнению db-engines.
Memcached
Суть: кеш
Я начинаю с третьего места — самой старой базы данных в рейтинге, релизнутой еще в 2003 году. Memcached — это не совсем БД, поскольку ее основная функция — автоудаление данных. Относитесь к ней как к массивному кешу фиксированного размера, встроенному в память. Когда память заканчивается, Memcached начинает удалять элементы с помощью алгоритма LRU. Он начинает с наименее используемых значений и удаляет их до тех пор, пока не освободит достаточно памяти для хранения новых значений. У Memcached нет опции persistence to disc, но в ней нет смысла, если база данных может удалять данные в любой момент.
Поскольку Memcached — это хранилище кеша, у него есть некоторые лимиты на размер ключа и значения:
- ключ до 250 байт;
- значение до 1 МБ.
Посмотрим, с чем БД справляется, а с чем нет.
Обязательно:
- [-] Способность надежно сохранять данные. Memcached автоматически удаляет наиболее старые данные. И раз уж мы говорим о базе данных, которая хранит все в памяти, надежным сохранением это назвать нельзя.
- [-] Способность надежно извлекать данные. Если они не были удалены, они будут выведены в результатах поиска.
- [+] Способность удалять данные. Тут все в порядке благодаря команде delete.
Желательно:
- [-] Способность выполнять запросы к данным. Здесь не поддерживается сопоставление ключей.
- [+] Способность обновлять данные. Можно обновить значение только целиком.
- [-] Наличие транзакций. Не в этой базе данных.
Riak
Суть: база данных «ключ-значение» с синхронизацией по нескольким центрам обработки данных
Riak кардинально отличается от Memcached и предназначен для решения других задач. Он ближе всего к базе данных в ее классическом понимании. Это БД «ключ-значение», распределенная между ЦОДами, предназначенная для постоянного хранения данных и обеспечивающая доступность даже за счет согласованности.
Типы данных
Базы данных «ключ-значение» рассматривают сохраненные значения как blob-объекты, но некоторые из них поддерживают типы данных, у которых есть определенная цель и отдельный API. В случае Riak это:
-
Flags — значения
true
илиfalse
. Могут использоваться только в типе map.
-
Registers — именованные двоичные файлы. Тоже могут использоваться только внутри map.
-
Counters — как следует из названия, инкрементные целые числа. Могут использоваться в map и как самостоятельное значение.
-
Sets — коллекция бинарных значений. Похожа на тип Counter; может использоваться самостоятельно или в map.
-
Maps — коллекция значений. В отличие от Sets, может содержать другие типы данных, даже другие maps.
-
HyperLogLog — вероятностная структура для проверки количества элементов в наборе.
Кластеризация
Riak поддерживает кластеризацию с настраиваемым уровнем согласованности. Как это делается? Поскольку кластер — это кольцевая архитектура, вроде этой:
Для настройки уровня согласованности нужно определить, сколько узлов должны принять операцию до ее подтверждения. По умолчанию — три.
Цель Riak из CAP — это A, и он пытается добиться ее конструированием кластера, в котором:
- узлы организуются в кольцо;
- нет главных узлов;
- любой узел может принять операции записи для любого ключа (нет главного узла для данного хеш-значения);
- обеспечивается одновременное выполнение операций записи на нескольких машинах для одного ключа.
Всё вышесказанное требует разрешать конфликты. Riak рекомендует при каждом удобном случае использовать пользовательские типы, но работает также и со значениями blob-объектов. Поведение при разрешении конфликтов можно настраивать, и диапазон возможных решений варьируется от метки времени и last-write-wins до варианта пусть решает клиент. Однако нетрудно найти людей, жалующихся на то, что Riak возрождает удаленные значения даже через несколько дней после удаления.
Теперь посмотрим на требования к БД.
Обязательно
- [+] Способность надежно сохранять данные. Одно из основных преимуществ Riak.
- [+] Способность надежно извлекать данные. Поскольку согласованность в Riak настраивается, этот пункт тоже частично засчитывается.
- [+] Способность удалять данные. Работает, хотя восстановление удаленных элементов — известная проблема.
Желательно
- [-] Способность выполнять запросы к данным. Сопоставление ключей не поддерживается, хотя поддерживается поиск по ключам с использованием Solr.
- [+] Способность обновлять данные. Возможно с пользовательскими типами. Невозможно с blob-объектами.
- [-] Наличие транзакций. Не поддерживаются.
Redis
Суть: скорость
Вряд ли кого-то удивит, что именно Redis занимает первое место. Полное название — REmote DIctionary Server. За годы Redis нарастил несколько функциональных возможностей, но он все равно остается словарем «ключ-значение» в памяти.
Архитектура
Redis подтверждает, что простота — это скорость. Здесь важно отметить, что это в основном хранилище в памяти с дополнительной сохраняемостью:
- в виде снимков, сделанных в определенный момент времени;
- Append Only File с асинхронными операциями записи;
- оба вышеуказанных варианта.
В Redis everything is a string, так что в нем реализован интерфейс манипулирования значениями непосредственно в базе данных, без необходимости отправлять их клиенту. И еще один момент, на который стоит обратить внимание: у Redis есть опция написания Lua-скриптов.
Структура данных
Как и в Riak, в Redis реализована пользовательская структура данных, но неструктурированные данные хранятся в нем как строка, а не как двоичный файл. Пользовательские структуры данных:
- Binary-safe string.
- Lists — коллекции строк, сортируемые по порядку вставки (список со ссылками).
- Sets — коллекции уникальных несортированных строк.
- Sorted sets — то же самое, что и Sets, с той разницей, что у каждой строки есть балл — число с плавающей запятой. Элементы сортируются по баллу, так что, в отличие от Sets Sorted, sets позволяют извлекать диапазоны, например, десять наибольших или наименьших значений.
- Hashes — maps, состоящие из полей, ассоциированных со значениями. И поле, и значение — это строки.
- Bit arrays — позволяет устанавливать, очищать, считать и находить первый набор или бит, не входящий в набор.
- HyperLogLogs — так же, как и в Riak.
Кластеризация
Подход к кластеризации, реализованный в Redis, отличается от подходов в двух предыдущих решениях:
- Все узлы взаимосвязаны.
- Значения автоматически передаются на несколько серверов.
- Реализована концепция ведущего и ведомого наборов данных.
- Нет гарантии полной согласованности, но ее можно достичь с помощью WAIT.
- Клиенту рекомендуется поддерживать в актуальном состоянии таблицу маршрутизации кластера.
- Способность обнаруживать не отвечающие и новые узлы.
- Узлы не выполняют запросы proxy-сервера, поэтому, если мы запросим ключ, отсутствующий на этом узле, сервер выдаст клиенту команду MOVED.
- Команды на несколько узлов и Lua-скрипты ограничены near keys (нет кросс-серверных операций).
- Асинхронная репликация.
- Только один узел принимает операцию записи для данного ключа.
Издатель-подписчик
У Redis в частности и у баз данных «ключ-значения» в целом (поскольку эта функция нетипична для баз данных) есть функция «издатель-подписчик». Наряду с такими функциями, как полная поддержка кластеров и сопоставление паттернов для подписок, это одна из важнейших отлично работающих функций Redis.
Теперь снова посмотрим на требования к БД.
Обязательно
- [-] Способность надежно сохранять данные. Это не основное назначение Redis. Представленные опции надежного сохранения данных не дают 100% гарантии восстановления данных, например, в случае отключения электричества.
- [+] Способность надежно извлекать данные. У Redis есть понятие главного узла для конкретного ключа, и если не путать его с таблицей маршрутизации, данные выводятся без сбоев.
- [+] Способность удалять данные. Просто работает.
Желательно
- [+] Способность выполнять запросы к данным. Redis использует API для поиска ключей, соответствующих паттерну. Важно помнить, что в результатах запроса выводятся ключи, а не значения.
- [+] Способность обновлять данные. Работает с пользовательскими типами и с blob-объектами. Поскольку Redis работает со строками, он выполняет операции с ними на стороне сервера, не гоняя данные к клиенту и обратно.
- [+] Наличие транзакций. В Redis есть транзакции — в Lua-скриптах и с использованием инструкции MULTI.
Сравнение всех БД
Функция | Memcached | Riak | Redis |
Лимит ключа |
250 байт |
Нет |
Нет |
Лимит значений |
1 МБ |
Нет |
512 МБ |
Сохраняемость |
Нет |
Да |
Дополнительно |
Транзакции |
Нет |
Нет |
Да |
Протокол подключения |
TCP/IP |
HTTP |
TCP/IP |
Сканирование ключей |
Нет |
С помощью Solr |
Да |
Скрипты |
Нет |
Нет |
Да (Lua) |
Схема данных |
Нет |
Пользовательские типы и blob-объект |
Пользовательские типы и blob-объект |
Сохраненные данные |
Двоичные |
Двоичные |
Строка |
Лицензия |
BSD 3-clause |
Apache 2 |
BSD 3-clause |
Кластер |
|
|
|
Информация о кластере |
Клиент знает все серверы в кластере |
Клиент знает некоторые узлы |
Клиент знает таблицу маршрутизации |
Архитектура клиента |
Без разделения ресурсов |
Кольцо |
All connected |
Согласованность |
Не применяется |
Настройка от итоговой до строгой согласованности |
Без гарантий |
Репликация |
Нет |
Конфигурируемая |
Асинхронная |
Синхронизация с несколькими ЦОДами |
Нет |
Да |
Да (только в корпоративной версии) |
ОС |
Windows/Linux/Unix |
Linux |
Windows/Linux |
Основные функции |
Автоудаление данных |
Репликация для нескольких ЦОДов |
Скорость, издатель -подписчик |
Предназначено для |
Серверов кеширования |
Хранилищ «ключ-значение» на несколько ЦОДов |
Высокой скорости |
Облачные базы данных от VK Cloud можно попробовать бесплатно. Мы начисляем пользователям при регистрации 3 000 бонусных рублей и будем рады, если вы попробуете сервис и дадите обратную связь.
Комментарии (6)
ivegner
14.09.2022 20:00Ничего не понял. Если у меня есть ученики, преподаватели, договора с теми и другими, предметы, уроки, расписание, учебные планы... Как мне предлагается их хранить в такой базе без отношений?
fireSparrow
14.09.2022 23:21Никак.
У каждой разновидности баз есть своя ниша. Конкретно для вашего случая следует выбрать классические реляционные базы.
SabMakc
15.09.2022 09:47То, что отношений нет на уровне СУБД - не значит, что отношения нельзя организовать на уровне ключей или хранимых объектов.
Да, придется самостоятельно следить за целостностью и согласованностью данных. Но это вполне реализуемо и достижимо.
Впрочем, полноценно заменить реляционную СУБД все равно не удастся скорее всего - главным образом потому, что SQL позволяет очень гибко данные получать и обрабатывать. А с KV-базами надо заранее планировать "где кто и как" будет данные получать и обрабатывать.
kevin_ek
15.09.2022 18:13+1в отличие от Sets Sorted, sets позволяют извлекать диапазоны
Тут запятая правильно поставлена? :)
Похоже как в классике: "Убить нельзя помиловать"...
ostinru
16.09.2022 16:34Удивился, увидев riak : последнее, что про нее слышал это то, что компания-разработчик Basho Technologies закрылась.
Пришлось погуглить - оказывается база еще живет. Ее подхватило коммьюнити и другие компании. Даже версию 3.0 выпустили.PS: Но, в этой статье все-таки про старую (2.x) версию riak-а написано: оригинальная статья 2017го года, когда у riak-а еще была материнская компания :)
anton19286
капитан?