Дизайн CQL для выборок без WHERE

#database-design #cassandra #cql #zebus #nosql

#database-design #cassandra #cql #zebus #nosql

Вопрос:

В настоящее время мы работаем над переписыванием каталога нашей одноранговой сервисной шины (Zebus).

У нас была реализация Cassandra / Thrift, и ее нужно было улучшить, чтобы соответствовать некоторым новым требованиям к загрузке, поэтому переписать ее с использованием CQL, казалось, было правильным решением.

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

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

Последний пункт был проблемой, потому что это означает запуск a SELECT * FROM "Subscriptions" , что означает перечисление строк из нескольких узлов (кстати, как CQL позволяет вам перечислять базовые строки Cassandra?) и происходит довольно медленно.

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

 CREATE TABLE IF NOT EXISTS "DynamicSubscriptions" (
    "UselessKey" boolean,
    "PeerId" text,
    "MessageTypeId" text,   
    "SubscriptionBindings" blob,
    PRIMARY KEY("UselessKey", "PeerId", "MessageTypeId")
);
  

Это довольно некрасиво, но делает свое дело, все оказываются в одном и том же «Бережливом ряду», что приводит к молниеносному чтению.

Итак, мой вопрос заключается в следующем: есть ли хороший способ создать CF с использованием CQL, если я хочу, чтобы мои данные можно было запрашивать очень быстро во время неограниченного выбора?

(Или, если вы считаете, что наш дизайн полностью испорчен, не стесняйтесь так и сказать).

Комментарии:

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

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

Ответ №1:

Итак, насколько я правильно понял, вы должны создать такую таблицу:
CREATE TABLE IF NOT EXISTS dynamic_subscriptions (
peer_id text,
message_type text,
bindings blob,
PRIMARY KEY (peer_id, message_type)
);
Он очень похож на ваш, но ключевое отличие заключается в определении первичного ключа. Первый столбец первичного ключа является ключом раздела, что означает, что все данные с одним и тем же ключом раздела будут храниться в одной и той же строке семейства физических столбцов. Остальные компоненты первичного ключа представляют собой столбцы кластеризации, которые используются для различения и сортировки данных в пределах одного ключа раздела.

Другими словами, преимущества этой схемы:

  • данные одноранговых узлов равномерно распределяются по кластеру
  • вы можете эффективно запрашивать данные одноранговых узлов (типы сообщений, типы сообщений с привязками), поскольку они сортируются и хранятся на одном узле
  • вы можете эффективно запрашивать один тип сообщения в контексте определенного однорангового узла
  • вы можете эффективно обновлять / удалять привязки для выбранного типа сообщения в контексте определенного однорангового узла

Я надеюсь, что это вам поможет.

Комментарии:

1. К сожалению, это первая схема, которую я придумал, но производительность SELECT * без ограничения WHERE была неприемлемой, отсюда и «уродство» моего дизайна.

2. Но, как вы сказали, вы собираетесь извлекать данные для определенного однорангового узла, не так ли?

3. Извините, я не совсем понял, что я имел в виду, что мне нужны оба, для одного данного узла и для всех из них.

4. Хорошо, итак, когда вы запрашиваете их все, вам нужно получить их все сразу или вы можете выполнить разбивку на страницы?

5. Что ж, лучше всего было бы как можно быстрее. Под разбиением на страницы вы подразумеваете выполнение нескольких запросов вручную или использование подкачки C * 2.0?

Ответ №2:

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

Один из способов, которым я могу думать об ограничении количества одноранговых узлов, для которых вы перечисляете подписки, основан на периоде времени. Обратите внимание, что это должен быть отдельный CF от того, который вы запрашиваете для получения информации о каждом узле. Золотое правило — один CF управляет одним запросом.

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

Подводя итог, вам понадобится CF, о котором упоминал Яцек Л. Кроме того, для отображения информации для нескольких одноранговых узлов вы могли бы использовать что-то вроде этого

 CREATE TABLE IF NOT EXISTS peer_subscriptions (
    message_type   text,
    connection_date  text,
    peer_id   text,
    bindings  blob,
    PRIMARY KEY ((message_type,connection_date),peer_id)
);