执行两个相同的请求,但DISTINCT关键字会产生意外结果。如果没有关键字,结果就可以了,但是使用DISTINCT,看起来像where子句被忽略了。为什么?
Cqlsh版本:
Connected to Test Cluster at localhost:9160.
[cqlsh 4.1.1 | Cassandra 2.0.6 | CQL spec 3.1.1 | Thrift protocol 19.39.0]
考虑的表:
DESCRIBE TABLE events;
CREATE TABLE events (
userid uuid,
"timestamp" timestamp,
event_type text,
data text,
PRIMARY KEY (userid, "timestamp", event_type)
) WITH
bloom_filter_fp_chance=0.010000 AND
caching='KEYS_ONLY' AND
comment='' AND
dclocal_read_repair_chance=0.000000 AND
gc_grace_seconds=864000 AND
index_interval=128 AND
read_repair_chance=0.100000 AND
replicate_on_write='true' AND
populate_io_cache_on_flush='false' AND
default_time_to_live=0 AND
speculative_retry='99.0PERCENTILE' AND
memtable_flush_period_in_ms=0 AND
compaction={'class': 'SizeTieredCompactionStrategy'} AND
compression={'sstable_compression': 'LZ4Compressor'};
表格内容:
SELECT * FROM events;
userid | timestamp | event_type | data
--------------------------------------+--------------------------+------------+------
aaaaaaaa-be1c-44ab-a0e8-f25cf6064b0e | 1970-01-17 09:06:17+0100 | toto | null
4271a78f-be1c-44ab-a0e8-f25cf6064b0e | 1970-01-17 09:06:17+0100 | toto | null
4271a78f-be1c-44ab-a0e8-f25cf6064b0e | 1970-01-17 09:07:17+0100 | toto | null
4271a78f-be1c-44ab-a0e8-f25cf6064b0e | 1970-01-17 09:08:17+0100 | toto | null
4271a78f-be1c-44ab-a0e8-f25cf6064b0e | 1970-01-17 09:09:17+0100 | toto | null
4271a78f-be1c-44ab-a0e8-f25cf6064b0e | 1970-01-17 09:10:17+0100 | toto | null
(6 rows)
Request1:没有DISTINCT的请求
SELECT userid FROM events WHERE timestamp > '1970-01-17 09:07:17+0100' ALLOW FILTERING;
userid
--------------------------------------
4271a78f-be1c-44ab-a0e8-f25cf6064b0e
4271a78f-be1c-44ab-a0e8-f25cf6064b0e
4271a78f-be1c-44ab-a0e8-f25cf6064b0e
(3 rows)
Request2:与DISTINCT相同的请求
SELECT DISTINCT userid FROM events WHERE timestamp > '1970-01-17 09:07:17+0100' ALLOW FILTERING;
userid
--------------------------------------
aaaaaaaa-be1c-44ab-a0e8-f25cf6064b0e
4271a78f-be1c-44ab-a0e8-f25cf6064b0e
(2 rows)
编辑1
这里有一些背景
这张表"事件"它受到大量写入的影响,每秒接收大约1k次插入,我有一个批处理脚本,每5分钟检查一次这些事件。
该批处理脚本有2个需求:
1-获取在过去5分钟内处于活动状态的所有用户ID(即最近5分钟内事件中出现的每个用户ID)
2-获取与这些用户ID相关的所有事件(不仅是最近5分钟)
我曾经有两个不同的表来处理这个问题。一个表"活跃的用户"第一次请求和"事件"像我在这里描述的第二个请求表。我的问题只是它需要我的服务器在收到一个事件时写入两个不同的表。所以我只使用事件表尝试了这个。
答案 0 :(得分:18)
这种情况就是这样,因为在Cassandra中,CQL DISTINCT
被设计为仅返回表(列族)的分区(行)键...它必须是唯一的。因此,WHERE
子句只能在与DISTINCT
一起使用时对分区键进行操作(在您的情况下,它不是非常有用)。如果您取出DISTINCT
,则WHERE
可用于评估每个分区键中的群集(列)键(尽管使用ALLOW FILTERING
)。
我觉得有必要提到ALLOW FILTERING
不是你应该做的很多事情......而且绝对不是在制作中。如果该查询是您需要经常运行的查询(在某个userids
之后查询timestamp
的事件),那么我建议您按event_type
分区数据:
PRIMARY KEY (event_type, "timestamp", userid)
然后,您将能够在没有ALLOW FILTERING
的情况下运行此查询。
SELECT userid FROM events WHERE event_type='toto' AND timestamp > '1970-01-17 09:07:17+0100'
在不了解您的应用程序或用例的情况下,这可能对您有用,也可能没有用。但请将其视为一个示例,并指出可能有更好的方法来构建模型以满足您的查询模式。请查看Patrick McFadin's article on timeseries data modeling,了解有关如何为此问题建模的更多建议。
答案 1 :(得分:4)
正如Aaron所解释的,当使用DISTINCT关键字时,您只能按分区键过滤。这背后的原因是DISTINCT查询背后的算法以及Cassandra将数据存储到磁盘/内存中的方式。
要理解这一点,我将做一个类比:
Cassandra存储类似于图书索引的信息。如果您正在搜索一个名为"我的第三章"你只需要查看索引的第一级,所以你只需要在一个相对较小的集合中进行迭代搜索。但是,如果您正在寻找一个名为"我的第四个子章节"属于"我的第二章"如果索引至少有2个级别,则必须在2个不同的集合中进行2次迭代搜索,两者都很小。你需要花费的时间越长,你可能需要的时间越长(你仍然可能很幸运,如果它在索引的开头就很快找到它,但在这种算法中你必须测试平均值和最坏的情况)并且索引需要越复杂。
Cassandra做了类似的事:Keyspace - >表 - >分区键 - >群集密钥 - >柱 你需要更深入,你需要更多的内存,而且需要更长的时间才能找到任何东西。用于执行DISTINCT查询的索引甚至可能只包含集合,直到分区键级别,因此只允许搜索分区键。
你需要意识到搜索任何有子章节的章节"我的第二小章" (与您的查询类比)仍然需要2级深度索引和2级迭代搜索。
如果他们决定支持在群集密钥上使用DISTINCT,那么您的查询就可以了。同时你必须在应用程序中过滤它们,可能是使用一个叫做set的内置类型或类似的东西来处理重复的值。
Aaron提出的解决方案(在时间戳之后使用userid作为集群密钥)也不是这个(在客户端过滤)使用DISTINCT快速机制。他的提议并不需要客户端过滤,因为它已经为你处理了这个问题,但它提供了两个主要缺点:它不提供向后兼容性,因为你必须重新创建表并使用一个恒定的分区键,因此不允许Cassandra在其节点之间分发这些数据。请记住,同一分区键的每个值都存储在同一节点中。