Обзор Riak

(Из ленты )

Riak — распределенная
opensource база данных, разработанная на Erlang и
спроектированная в расчете на:

  • Высокую доступность и устойчивость к сбоям;
  • Масштабируемость и простоту обслуживания;
  • Универсальность.

У проекта отличная официальная документация на английском, далее же в этой статье я расскажу об основных её особенностях чуть подробнее, а также хитростях и подводных камнях, выявленных в процессе применения на практике (с перспективы веб-разработки).

Высокая доступность и устойчивость к сбоям

  • Все данные в кластере реплицируются по принципу соседей на хэш
    кольце (см. логотип для иллюстрации) и даже в случае сбоев
    доступны посредством интеллектуального перенаправления запросов
    внутри кластера.
  • В случае возникновения коллизий из-за разрыва сетевого соединения
    или просто одновременной записи, на запрос получения данных может
    вернуться несколько версий
    и приложение само может решить как их
    объединить или какую версию использовать.

Масштабируемость и простота обслуживания

  • Добавление нового сервера тривиально путем копирования конфига и
    одной команды.
  • Перераспределение данных и все остальное прозрачно происходит за
    сценой.
  • Минимальный рекомендуемый размер Riak кластера — 5 серверов, меньшее
    количество не дает раскрыть весь потенциал.
  • Одинаково легко обслуживать как маленький, так и большой кластер.
  • Есть коммерческая Enterprise версия с поддержкой от Basho,
    компании-разработчика Riak (изначально выходцы из Akamai),
    равноправной зашифрованной репликацией между датацентрами и
    поддержкой SNMP.
  • Есть встроенный веб-интерфейс для мониторинга и управления
    кластером, у меня правда так и не дошли руки его освоить:

Универсальность

  • Схема отсутствует, ключи и данные — произвольные бинарные строки.
    Ключи располагаются в пространствах имен (bucket).
  • Сериализация — на усмотрение разработчика, популярные варианты —
    Erlang’овский BERT, JSON для других платформ, можно использовать
    просто как файловую систему.
  • Модульная система хранилищ данных, альтернатив много, основная —
    Google LevelDB; еще интересный
    вариант с хранением полностью в оперативной памяти — получается
    продвинутый распределенный кэш с репликацией, поиском и пр.
  • Гибко настраиваемое количество узлов кластера, которые должны
    подтвердить успешность операции, чтобы она считалась успешной: можно
    указывать для всего кластера, пространства имен и даже конкретного
    запроса. Riak в любом случае остается eventually consistent базой
    данных (AP из CAP теоремы), но с возможностью управлять балансом
    производительности операций и надежностью выполнения запросов.
  • Три интерфейса доступа (API):
    • Google ProtocolBuffers — для основного
      использования в боевых условиях.
    • HTTP REST — для использования в
      языках, где нет готового клиента на ProtocolBuffers и для того,
      чтобы по-быстрому что-то посмотреть из консоли через curl. Хотя
      по факту клиенты для большинства языков программирования есть и
      проще делать запросы через интерпретатор.
    • Еще есть прямой интерфейс Erlang-сообщений, но даже из самого
      Erlang им пользоваться не рекомендуют, не говоря уже о
      реализациях Erlang node (BERT) на других платформах.
  • Вместе с данными хранятся метаданные для разных целей, которые
    используются в соответствующих типах запросов:

    • Векторные часы
      для разрешения конфликтов версий данных (обязательно, есть
      автоматическое разрешение);
    • Индекс для полнотекстного поиска (концептуально позаимствован у
      Lucene/Solr, опционально);
    • Индекс для простых выборок (по бинарным и числовым полям,
      опционально);
    • Связанные ключи (отдаленный аналог внешних ключей,
      опционально).
  • Встроенная поддержка MapReduce, фазы можно
    реализовывать на Erlang или
    JavaScript; для обоих языков есть библиотека с
    наиболее популярными случаями, которые можно использовать для
    образца.
  • Есть поддержка выполнения операций до/после операций записи/чтения
    (hooks), чаще всего используются для построения полнотекстного
    индекса, но можно реализовать и свои, специфичные для приложения.

Недокументированные возможности

Пока я их нашел всего две:

  • Счетчики: как такового API в для увеличения/уменьшения числовых
    значений (increment/decrement) в Riak нет, так как он не лезет
    внутрь хранящихся данных. Зато есть векторные часы, которые растут с
    каждой операцией записи по ключу. Чтобы реализовать увеличение
    (increment) необходимо записать в Riak пустую бинарную строку с
    опцией return_body, и у вернувшегося значения сложить все поля в
    векторных часах. Пример на
    Erlang
    . Если нужно еще и
    уменьшение (decrement) этого можно добиться с помощью пары счетчиков
    «плюс и минус» и вычитать второе значение из первого. Для авто
    инкремента основных ключей не самый лучший вариант, но для не особо
    критичных случаев вполне себе работает.
  • Выборка по списку ключей (multiget)такого API тоже нет, но
    здесь на выручку приходит MapReduce. Это, пожалуй, наиболее
    популярное его применение. На вход подаем имеющийся список ключей и
    используем фазы из готовой библиотеки: reduce_set_union и
    map_identity. Данные возвращаются неотсортированные  и требуют
    небольшой обертки на выходе, но все равно это намного быстрее, чем
    последовательно проходить по списку ключей и делать для каждого
    обычный get. Пример на Erlang.

Буду рад, если Вы поможете мне дополнить этот список, оставив известные
Вам подобные трюки в комментариях.

Подводные камни

  • Если в Вашем приложении необходима функциональность постраничного
    просмотра отсортированных данных
    (pagination), то будьте готовы
    реализовать её на клиенте. То есть Riak быстро сделал нужную выборку
    всех «страниц» и уже на клиенте её придется отсортировать и выкинуть
    лишнее. Вообще в большинстве случаев результаты запросов к Riak
    приходят в произвольном порядке из-за его распределенной природы.
  • В продолжение к предыдущему: в REST Solr интерфейсе есть
    аргументы (в ProtoBuf это тоже добавили в одной из последних
    версий), которые, казалось бы, достаточны для реализации
    постраничного просмотра: sort, start, rows — что еще
    нужно? На практике оно работает не так, как было бы логично.
    Сортировка по значению (заданная в sort) применяется ПОСЛЕ того, как
    была отсчитана страница по start и rows. Они отмеряются по ключам
    или рейтингу значения в полнотекстном поиске и никак иначе. С тем же
    успехом эти 5-10 значений можно очень быстро отсортировать и на
    клиенте. Зачем-то это может быть и нужно, но в моем случае оказалось
    совершенно бесполезно.
  • У Riak есть 4 основных типа запросов: простой
    get/set, полнотекстовый поиск, вторичные ключи (secondary
    indices)
    , МapReduce и проход по связанным ключам (link walking).

    • Если Ваши данные являются сериализованным JSON, BERT или XML, то
      в большинстве случаев Вам нужны лишь первые два из них,
      исключение — упомянутая выше выборка по списку ключей через
      MapReduce.
    • Основной сценарий использования вторичных индексов — метаданные
      к произвольным неструктурированным бинарным данным, например в
      случае с аналогом файловой системы. Либо совсем примитивные
      случаи, когда правда нужно сделать простую выборку по одному
      целочисленному полю, что бывает редко.
    • Если данные сериализованы, то связанные ключи проще хранить
      внутри данных, а не средствами СУБД. Разницы в
      производительности нет, в итоге делается тот же MapReduce с теми
      же фазами.
  • Хоть Riak «из коробки» и правда надежнее многих других СУБД и 1-2
    упавших/отключенных сервера в кластере внешне практически не
    заметны, есть одно но. Если один узел упал — соединения всех
    подключенных к нему клиентов теряются. Два основных
    пути преодоления этого момента:

    • Если кластер клиентов и кластер Riak расположены на разных
      серверах, то между ними можно поставить отказоустойчивый TCP
      балансировщик нагрузки, в частности HAProxy или
      IPVS здесь наиболее органично вписываются.
    • Если на одних и тех же, то есть вариант поставить балансировщик
      нагрузки перед клиентами (для веба возможно и в HTTP/HTTPS
      режиме), а каждый клиент подключается к своему локальному
      серверу Riak и если один, другой или оба сразу упали, то
      отрубать весь физический сервер целиком.

Выводы

Riak отлично подходит для многих вариантов использования, как в Интернет
среде, так и в смежных вроде телекома. Обладает отличным набором
положительных «черт характера», о которых шла речь в начале статьи.
Прекрасно справляется с большим потоком как операций записи, так и
операций чтения.

Как уже упоминалось, практически единственный сценарий, где Riak совсем
не справляется, это выборки по большим объемам данных с сортировкой и
постраничным выводом. Но даже в этом случае никто не мешает использовать
отдельный сервис, который будет индексировать нужным образом данные и
подготавливать список идентификаторов для последующей multiget выборки
из Riak. К слову, проекты по этой части уже появляются, например
Yokozuna — интеграция
полноценного Solr с Riak (Riak Search — лишь частичный порт Solr+Lucene
на Erlang)
.

Дополнительная информация по теме

Источник: Обзор Riak