5.1 Распределение данных

Рассмотрим, как данные распределяются по узлам кластера в зависимости от ключа. Кассандра позволяет задавать стратегию распределения. Первая стратегия распределяет данные в зависимости от md5 значения ключа — случайный разметчик. Вторая стратегия учитывает битовое представление ключа — порядковый разметчик.

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

Кассандра использует технику под названием согласованное хеширование (consistent hashing), чтобы распределять данные. Это позволяет разделить данные между узлами и сделать так, что при добавлении и удалении нового узла пересылаемые данные будут небольшими. Каждому узлу ставится в соответствие метка (token), которая разбивает на части множество всех md5 значений ключей.

Обычно используется RandomPartitioner, который вычисляет 128-битный md5 для каждого ключа. Для определения, где хранить данные, просто сравнивают метки узлов с md5 значением ключа. Число выбранных узлов равно уровню репликации (replication factor). Уровень репликации задаётся для каждого пространства ключей и позволяет регулировать избыточность данных.

Перед тем, как добавить узел в кластер, необходимо задать ему метку. Количество данных, хранящихся на узле, зависит от процента ключей, покрываемых промежутком между этой меткой и следующей. Все метки для кластера называются кольцом (ring).

Ниже приведена иллюстрация, показывающая кольцо кластера из 6 узлов с равномерно распределенными метками, полученная с помощью встроенной утилиты nodetool.

5.2 Согласованность данных при записи

Узлы кластера кассандры равноправны, и любой из них может подключить клиента для чтения или записи. Запросы проходят через этап координации. В этот момент сервер определяет ключ, разметчик и метки, чтобы знать на каких узлах должны располагаться данные, и отправляет запросы на эти узлы.

Тот узел, который выполняет координацию, называется координатором. А те узлы, на которых будут сохранены данные, называются узлами-реплик. Координатор может быть в любое время одним из узлов-реплик, в зависимости от ключа, разметчика и меток.

Для каждого запроса, будь то чтение или запись, можно установить уровень согласованности данных.

Для записи нам нужно проверить успешность операции (что данные записаны) на нескольких репликах (узлах). Существуют следующие уровни согласованности для записи:

  • ONE — Координатор отправляет запросы всем узлам-репликам, но после получения подтверждения от первого узла сразу же возвращает управление пользователю.
  • TWO — Координатор ждет подтверждения от двух первых узлов, прежде чем дать разрешение.
  • THREE — Так же, но координатор дожидается подтверждения от трех первых узлов, прежде чем возвратить управление.
  • QUORUM — Кворум собирается: координатор ожидает подтверждения записи от более чем половины реплик, то есть round(N / 2) + 1, где N - уровень репликации.
  • LOCAL_QUORUM —Координатор ждет, пока больше половины узлов-реплик в том же центре обработки данных, где находится координатор, подтвердят его (каждый запрос имеет свое). Это помогает избежать задержек, связанных с пересылкой данных в другие центры обработки. Работа с несколькими центрами обработки данных коснулась в этой статье.
  • EACH_QUORUM — Координатор ждет, чтобы больше половины узлов-реплик в каждом центре обработки данных подтвердили это независимо.
  • ALL — Координатор ожидает, чтобы все узлы-реплик подтвердили.
  • ANY — Координатор позволяет записать данные, даже если не все узлы-реплики отвечают. Он ждет первого ответа от одного из узлов-реплик или когда данные будут сохранены с помощью "направленной отправки" (hinted handoff) на координаторе.

5.3 Согласованность данных при чтении

Чтобы читать, уровень согласования будет влиять на количество реплик, с которых будет производиться чтение. Для чтения есть следующие уровни согласования:

  • ONE — Координатор отправляет запросы к ближайшему реплике-узлу. Другие реплики также читаются для чтения с исправлением (read repair), с вероятностью, указанной в конфигурации кассандры.
  • TWO — Координатор посылает запросы двум ближайшим узлам. Значение с большей меткой времени будет выбрано.
  • THREE — То же, что и в предыдущем варианте, только с тремя узлами.
  • QUORUM — Для сбора кворума координатор посылает запросы больше чем половине узлов-реплик, то есть round(N / 2) + 1, где N - это уровень репликации.
  • LOCAL_QUORUM — Кворум собирается в центре обработки данных, где происходит координация. Данные возвращаются с последней меткой времени.
  • ACH_QUORUM — Координатор получает данные после того, как в каждом из центров обработки данных состоялось собрание кворума.
  • ALL — Координатор считывает информацию со всех реплик и возвращает данные.

Если количество узлов, от которых получается подтверждение о записи и количество узлов, от которых происходит чтение, больше чем уровень репликации, то мы можем гарантировать, что после записи новое значение всегда будет считано. Это называется строгой согласованностью (strong consistency). Если строгой согласованности нет, то есть возможность, что чтение вернет устаревшие данные.

В конце концов, значение будет одинаково между репликами после координационного ожидания. Это называется итоговой согласованностью. Если не все узлы будут доступны для записи, то будут использованы средства восстановления, такие как чтение с исправлением и анти-энтропийное восстановление узла. Об этом позже.

При QUORUM будет всегда существовать строгая согласованность для чтения и записи. При записи ALL и чтении ONE строгая согласованность будет также сохраняться. Операции чтения будут быстрее и более доступны. То есть, при большем количестве вышедших из строя узлов, чтение все еще будет выполнено с помощью QUORUM.

Для операций записи требуются все рабочие узлы-реплики. При записи ONE, чтении ALL необходима строгая согласованность. Это позволяет выполнять операции записи быстрее и повышает доступность записи, так как достаточно подтвердить, что операция записи прошла хотя бы на одном из серверов.

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


NoSQL и Apache Cassandra (подключение и настройка)