Запросить таблицу по-разному или заказы в Кассандре

Я недавно начал играть с Кассандрой. Насколько я понимаю, в таблице Cassandra вы определяете 2 ключа, которые могут быть либо одним столбцом, либо составными:

  1. Ключ разделения: определяет, как распределять данные по узлам
  2. Ключ кластеризации: определяет, в каком порядке записываются записи одного и того же ключа разделения (то есть внутри одного узла). Это также порядок, в котором записи будут прочитаны.

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

Но что делать, если мне нужно выполнить 2 разных запроса к данным из таблицы. Каков наилучший способ решить эту проблему при использовании Cassandra?

Пример сценария

Допустим, у меня есть простая таблица, содержащая записи, которые написали пользователи:

CREATE TABLE posts (
  username varchar,
  creation timestamp,
  content varchar,
  PRIMARY KEY ((username), creation)
);

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

SELECT * FROM posts WHERE username='luke' [ORDER BY creation DESC];

Запросы

Но что, если мне нужно получить все сообщения независимо от имени пользователя, в порядке времени:

Запрос (1): SELECT * FROM posts ORDER BY creation;

Или получить сообщения в алфавитном порядке содержания:

Запрос (2): SELECT * FROM posts WHERE username='luke' ORDER BY content;

Я знаю, что это невозможно, учитывая таблицу, которую я создал, но каковы альтернативы и лучшие методы для решения этой проблемы?

Идеи решения

Вот несколько идей, порожденных моим воображением (просто чтобы показать, что, по крайней мере, я пытался):

  • Запросы с предложением IN, чтобы выбрать сообщения от многих пользователей. Это может помочь в Query (1). При использовании предложения IN вы можете получать результаты, отсортированные по всему миру, если отключите подкачку страниц. Но использование предложения IN быстро приводит к плохой производительности, когда число имен пользователей растет.
  • Поддерживая полные копии таблицы для каждого запроса, каждая копия использует свой собственный PRIMARY KEY, адаптированный к запросу, который он пытается обслужить.
  • Наличие основной таблицы с UUID в качестве ключа разделения. Затем создайте меньшие копии таблицы для каждого запроса, которые содержат только (ключевые) столбцы, полезные для их собственного порядка сортировки, и UUID для каждой строки основной таблицы. Таблицы меньшего размера будут служить только "индексами сортировки" для запроса списка UUID в качестве результата, который затем может быть получен с использованием основной таблицы.

Я новичок в NoSQL, я просто хотел бы знать, как правильно / долговечно / эффективно сделать это.

3 ответа

Решение

Вопрос 1:

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

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

Основная идея заключается в том, что вы вносите изменения в соответствии с вашим вариантом использования. Например:

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

Я приведу конкретный пример с yyyy-mm-dd в качестве PK:

Таблица теперь будет:

CREATE TABLE posts_by_creation (
  creation_year int,
  creation_month int,
  creation_day int,
  creation timeuuid,
  username text,  -- using text instead of varchar, they're essentially the same
  content text,
  PRIMARY KEY ((creation_year,creation_month,creation_day), creation)
)

Я изменил создание на timeuuid, чтобы гарантировать уникальную строку для каждого события создания поста. Если бы мы использовали только временную метку, вы могли бы теоретически перезаписать существующую запись пост-создания здесь.

Теперь мы можем вставить ключ раздела (PK): creation_year, creation_month, creation_day на основе текущего времени создания:

INSERT INTO posts_by_creation (creation_year, creation_month, creation_day, creation, username, content) VALUES (2016, 4, 2, now() , 'fromanator', 'content update1';
INSERT INTO posts_by_creation (creation_year, creation_month, creation_day, creation, username, content) VALUES (2016, 4, 2, now() , 'fromanator', 'content update2';

now () - это CQL-функция для генерации timeUUID, вы, вероятно, захотите сгенерировать ее в приложении, а затем проанализировать yyyy-mm-dd для PK и затем вставить timeUUID в кластеризованный столбец.

Для случая использования этой таблицы, скажем, вы хотите увидеть все изменения сегодня, ваш CQL будет выглядеть так:

SELECT * FROM posts_by_creation WHERE creation_year = 2016 AND creation_month = 4 AND creation_day = 2;

Или, если вы хотите найти все изменения сегодня после 17:00 по центральному:

SELECT * FROM posts_by_creation WHERE creation_year = 2016 AND creation_month = 4 AND creation_day = 2 AND creation> = minTimeuuid ('2016-04-02 5: 00-0600');

minTimeuuid () - это еще одна функция cql, она создаст наименьший возможный timeUUID за указанное время, это гарантирует, что вы получите все изменения за это время.

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

Вопрос 2:

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

Наконец, если вы не пользуетесь Cassandra 3.x+ или не хотите использовать материализованные представления, вы можете использовать пакеты Atomic для обеспечения согласованности данных по нескольким ненормализованным таблицам (для этого они и предназначены). Так что в вашем случае это будет оператор BATCH с 3 вставками одних и тех же данных в 3 разные таблицы, которые поддерживают ваши шаблоны запросов.

SELECT * FROM posts ORDER BY creation; приведет к полной проверке кластера, потому что вы не предоставите ключ раздела. И ORDER BY пункт в этом запросе не будет работать в любом случае.

Ваше требование I need to get all posts regardless of the username, in order of time это очень трудно достичь в распределенной системе, это предполагает:

  1. получить все сообщения пользователей и переместить их в один узел (координатор)
  2. упорядочить их по дате
  3. занять топ N последних сообщений

Точка 1. Требуется полное сканирование таблицы. Действительно, пока вы не получите все записи, порядок не может быть достигнут. Если вы не используете кластерный столбец Cassandra для заказа во время вставки. Но в этом случае это означает, что все сообщения хранятся в одном разделе, и этот раздел будет расти вечно...

запрос SELECT * FROM posts WHERE username='luke' ORDER BY content; возможно с использованием денормализованной таблицы или с новой функцией материализованного представления ( http://www.doanduyhai.com/blog/?p=1930)

Решение заключается в создании еще одной таблицы для поддержки ваших запросов.

За SELECT * FROM posts ORDER BY creation;вам может понадобиться специальный столбец для группировки, например, по месяцам и годам, например PRIMARY KEY((year, month), timestamp) таким образом, у cassandra будет лучшая производительность при чтении, потому что ему не нужно сканировать весь кластер, чтобы получить все данные, это также сохранит передачу данных между узлами.

Такой же как SELECT * FROM posts WHERE username='luke' ORDER BY content;, вы должны создать еще одну таблицу для этого запроса. Все столбцы могут совпадать с вашей первой таблицей, но с другим первичным ключом, поскольку вы не можете упорядочить по столбцу, который не является столбцом кластеризации.

Другие вопросы по тегам