具有 MAX() 和 GROUP BY() 的 select 是有效的还是会读取所有行
Is a select with MAX() and GROUP BY() efficient or will it read all rows
我有一个像这样创建的 cassandra table:
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;
我想知道这是否会读取所有消息并只提取最新消息,或者它是否足够聪明,只提取最新消息。
我问的原因是因为用以下值填充 table:
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 行。有没有办法优化这个?也许更改我的架构?
我能想到的两个选项是让您制作另一个 table 作为每个 userID 和 peerID 的最大记录的索引。这两个字段将构成您的分区键,然后将包含您在消息中找到该用户 ID 和 peerID 的最大记录 table 所需的其余数据。每当您将数据放入其中时,数据都会被更新,因此您总是只将最新的消息写入 table,并且它始终是最大值。您可以做的另一件事是将最后一条消息完全存储在那里,然后您根本不必在那里引用您的消息 table 来获取实际数据。与我之前提到的相同的分区键,也只需将实际消息也写在那里。
所以这是一个想法;将您的分区键更改为 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)
注意:最后一个查询是一个多键查询,仅用于演示目的,显然不能在大型生产集群中完成。
我有一个像这样创建的 cassandra table:
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;
我想知道这是否会读取所有消息并只提取最新消息,或者它是否足够聪明,只提取最新消息。
我问的原因是因为用以下值填充 table:
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 行。有没有办法优化这个?也许更改我的架构?
我能想到的两个选项是让您制作另一个 table 作为每个 userID 和 peerID 的最大记录的索引。这两个字段将构成您的分区键,然后将包含您在消息中找到该用户 ID 和 peerID 的最大记录 table 所需的其余数据。每当您将数据放入其中时,数据都会被更新,因此您总是只将最新的消息写入 table,并且它始终是最大值。您可以做的另一件事是将最后一条消息完全存储在那里,然后您根本不必在那里引用您的消息 table 来获取实际数据。与我之前提到的相同的分区键,也只需将实际消息也写在那里。
所以这是一个想法;将您的分区键更改为 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)
注意:最后一个查询是一个多键查询,仅用于演示目的,显然不能在大型生产集群中完成。