Эффективен ли выбор с MAX () и GROUP BY () или он будет читать все строки - PullRequest
2 голосов
/ 13 апреля 2019

У меня есть созданная таблица кассандры, например:

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 строк.Есть ли способ оптимизировать это?Может быть, изменить мою схему?

Ответы [ 2 ]

0 голосов
/ 15 апреля 2019

Итак, вот мысль; измените ключ разделения на 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)

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

0 голосов
/ 14 апреля 2019

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

...