Команда 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)


  1. anton19286
    14.09.2022 17:23

    Цель Riak из CAP — это A

    капитан?


  1. ivegner
    14.09.2022 20:00

    Ничего не понял. Если у меня есть ученики, преподаватели, договора с теми и другими, предметы, уроки, расписание, учебные планы... Как мне предлагается их хранить в такой базе без отношений?


    1. fireSparrow
      14.09.2022 23:21

      Никак.
      У каждой разновидности баз есть своя ниша. Конкретно для вашего случая следует выбрать классические реляционные базы.


    1. SabMakc
      15.09.2022 09:47

      То, что отношений нет на уровне СУБД - не значит, что отношения нельзя организовать на уровне ключей или хранимых объектов.

      Да, придется самостоятельно следить за целостностью и согласованностью данных. Но это вполне реализуемо и достижимо.

      Впрочем, полноценно заменить реляционную СУБД все равно не удастся скорее всего - главным образом потому, что SQL позволяет очень гибко данные получать и обрабатывать. А с KV-базами надо заранее планировать "где кто и как" будет данные получать и обрабатывать.


  1. kevin_ek
    15.09.2022 18:13
    +1

    в отличие от Sets Sorted, sets позволяют извлекать диапазоны

    Тут запятая правильно поставлена? :)

    Похоже как в классике: "Убить нельзя помиловать"...


  1. ostinru
    16.09.2022 16:34

    Удивился, увидев riak : последнее, что про нее слышал это то, что компания-разработчик Basho Technologies закрылась.
    Пришлось погуглить - оказывается база еще живет. Ее подхватило коммьюнити и другие компании. Даже версию 3.0 выпустили.

    PS: Но, в этой статье все-таки про старую (2.x) версию riak-а написано: оригинальная статья 2017го года, когда у riak-а еще была материнская компания :)