#cassandra #cql #cassandra-3.0
#cassandra #cql #cassandra-3.0
Вопрос:
У меня есть созданная таблица cassandra следующим образом:
create table messages
(user_id int, peer_id int, send_on timestamp, message text,
PRIMARY KEY (user_id, peer_id, send_on))
WITH CLUSTERING ORDER BY (peer_id ASC, send_on DESC);
и заполнен данными.
Я хочу запросить последнее сообщение для каждого peer_id для данного пользователя, и то, что я придумал, было:
select peer_id, max(send_on), message
from messages
where user_id = 1 group by peer_id;
Мне было интересно, будет ли это считывать ВСЕ сообщения и просто извлекать последние, или он достаточно умен, чтобы получать только последнее сообщение.
Причина, по которой я спрашиваю, заключается в том, что заполните таблицу следующими значениями:
1, 1, now(), hello 1
1, 1, now(), hello 2
1, 1, now(), hello 3
1, 2, now(), hello 4
1, 2, now(), hello 5
1, 2, now(), hello 6
...
1, 3, now(), hello 9
И когда я запускаю запрос, я вижу ожидаемый результат:
select peer_id, max(send_on), message from messages where user_id = 1 group by peer_id;
peer_id | system.max(send_on) | message
--------- --------------------------------- ---------
1 | 2019-04-13 19:20:48.567000 0000 | hello 3
2 | 2019-04-13 19:21:07.929000 0000 | hello 6
3 | 2019-04-13 19:21:22.081000 0000 | hello 9
(3 rows)
Однако с включенной трассировкой я вижу:
activity | timestamp | source | source_elapsed | client
------------------------------------------------------------------------------------------------------------------------------- ---------------------------- ----------- ---------------- -----------
Execute CQL3 query | 2019-04-13 19:24:54.948000 | 127.0.0.1 | 0 | 127.0.0.1
Parsing select peer_id, max(send_on), message from messages where user_id = 1 group by peer_id; [Native-Transport-Requests-1] | 2019-04-13 19:24:54.956000 | 127.0.0.1 | 8812 | 127.0.0.1
Preparing statement [Native-Transport-Requests-1] | 2019-04-13 19:24:54.957000 | 127.0.0.1 | 10234 | 127.0.0.1
Executing single-partition query on messages [ReadStage-2] | 2019-04-13 19:24:54.962000 | 127.0.0.1 | 14757 | 127.0.0.1
Acquiring sstable references [ReadStage-2] | 2019-04-13 19:24:54.962000 | 127.0.0.1 | 14961 | 127.0.0.1
Skipped 0/0 non-slice-intersecting sstables, included 0 due to tombstones [ReadStage-2] | 2019-04-13 19:24:54.962000 | 127.0.0.1 | 15211 | 127.0.0.1
Merged data from memtables and 0 sstables [ReadStage-2] | 2019-04-13 19:24:54.963000 | 127.0.0.1 | 15665 | 127.0.0.1
Read 9 live rows and 0 tombstone cells [ReadStage-2] | 2019-04-13 19:24:54.963000 | 127.0.0.1 | 15817 | 127.0.0.1
Request complete | 2019-04-13 19:24:54.964448 | 127.0.0.1 | 16448 | 127.0.0.1
Похоже, что он считывает ВСЕ 9 строк. Есть ли способ оптимизировать это? Может быть, изменить мою схему?
Ответ №1:
Два варианта, о которых я могу подумать, заключаются в том, чтобы вы создали другую таблицу, которая действует как индекс для записи max для каждого идентификатора пользователя и идентификатора пользователя. Эти два поля будут составлять ваш ключ раздела, а затем будут содержать остальные данные, необходимые для поиска максимальной записи в вашей таблице сообщений для этого идентификатора пользователя и идентификатора пользователя. Данные будут обновляться каждый раз, когда вы добавляете в них данные, поэтому вы всегда просто записываете самое последнее сообщение в эту таблицу, и оно всегда будет максимальным. Другая вещь, которую вы могли бы сделать, это просто сохранить последнее сообщение там вообще, и тогда вам вообще не нужно ссылаться на свою таблицу сообщений для получения фактических данных. Тот же ключ раздела, о котором я упоминал ранее, просто напишите там фактическое сообщение.
Комментарии:
1. Да, я думаю, мне придется перейти к другой таблице. В любом случае, я думаю, это способ Cassandra
Ответ №2:
Итак, вот мысль; измените ключ вашего раздела на user_id
и peer_id
, и тогда вы сможете использовать PER PARTITION LIMIT
конструкцию. Это будет считывать только одну строку назад (для каждого раздела), и тогда вам также не придется использовать MAX
, поскольку первая строка будет самой последней из-за CLUSTERING ORDER BY (send_on DESC)
:
> CREATE TABLE messages
(user_id int, peer_id int, send_on timestamp, message text,
PRIMARY KEY ((user_id, peer_id), send_on))
WITH CLUSTERING ORDER BY (send_on DESC);
> SELECT peer_id, send_on, message
FROM messages
WHERE user_id = 1 AND peer_id=1
PER PARTITION LIMIT 1;
peer_id | send_on | message
--------- --------------------------------- ---------
1 | 2019-04-15 15:21:40.350000 0000 | hello 3
(1 rows)
> SELECT peer_id, send_on, message
FROM messages PER PARTITION LIMIT 1;
peer_id | send_on | message
--------- --------------------------------- ---------
3 | 2019-04-15 15:21:40.387000 0000 | hello 9
2 | 2019-04-15 15:21:40.365000 0000 | hello 6
1 | 2019-04-15 15:21:40.350000 0000 | hello 3
(3 rows)
Примечание: последний запрос — это запрос с несколькими ключами, выполняемый только для демонстрационных целей, и, очевидно, это не то, что нужно делать в большом производственном кластере.