我有一个以下 CQL 表(为了清楚起见,稍微简化了一点):
CREATE TABLE test_table (
user uuid,
app_id ascii,
domain_id ascii,
props map<ascii,blob>,
PRIMARY KEY ((user), app_id, domain_id)
)
这个想法是该表将包含许多用户(即行,例如,数千万)。每个用户都会有几个感兴趣的域,每个域会有几个应用程序。对于每个用户/域/应用程序,都会有一小组属性。
我需要扫描整个表并为给定的 app_id 和 domain_id 分块加载其内容。我的想法是使用 TOKEN 函数能够在多次迭代中读取整个数据集。所以,像这样:
SELECT props FROM test_table WHERE app_id='myapp1'
AND domain_id='mydomain1'
AND TOKEN(user) > -9223372036854775808
AND TOKEN(user) < 9223372036854775807;
我假设这个查询会很有效,因为我指定了行键的范围,并且通过指定集群键的值,我有效地指定了列范围。但是,当我尝试运行此查询时,我收到错误消息“错误请求:无法执行此查询,因为它可能涉及数据过滤,因此可能具有不可预测的性能。如果您想在性能不可预测的情况下执行此查询,请使用 ALLOW FILTERING” .
我对 Cassandra 的经验有限,我假设这种查询将映射到 get_range_slices() 调用,该调用接受切片谓词(即由我的 app_id/domain_id 值定义的列范围)和由我的令牌范围定义的键范围. 似乎我误解了这种查询的处理方式,或者我误解了 get_range_slices() 调用的效率。
更具体地说,我的问题是: - 如果这个数据模型对我想到的查询类型有意义 - 如果这个查询预计是有效的 - 如果它是有效的,那么为什么我会收到这个错误消息我允许过滤
我对最后一个的唯一猜测是需要从结果中跳过没有给定 app_id/domain_id 组合的行。
- - 更新 - -
感谢所有的评论。我一直在对此进行更多研究,但仍有一些我不完全理解的东西。
在给定的结构中,我试图从我的数据集中获得一个矩形区域(假设所有行都具有相同的列)。其中矩形的顶部和底部由标记范围(范围)确定,左侧/右侧定义为列范围(切片)。因此,这应该自然地转换为 get_range_slices 请求。我的理解(如果我错了,请纠正我)CQL 要求我放置 ALLOW FILTERING 子句的原因是因为会有不包含我要查找的列的行,因此必须跳过它们。而且由于没有人知道在找到符合我的标准(在给定范围内)的行之前是否必须跳过每隔一行或前一百万行 - 这就是导致不可预测的延迟甚至可能超时的原因。我对吗?我试图编写一个执行相同类型查询但使用低级 Astyanax API 的测试(在同一张表上,我必须读取使用 CQL 生成的数据,结果非常简单)并且这个测试确实有效- 除了它返回没有列的键,其中行不包含我要求的列切片。当然,我必须根据起始令牌和限制来实现某种简单的分页,以便以小块的形式获取数据。
现在我想知道 - 再次考虑到我需要处理数以千万计的用户:部分“旋转”这个表并将其组织成这样会更好:
行键:domain_id + app_id + 分区号(类似于 hash(user) mod X) 聚类键:列分区号(类似于 hash(user) >> 16 mod Y)+ 用户
对于“列分区号”......我不确定它是否真的需要。我假设如果我使用这个模型,每个域 + 应用程序组合的行数会相对较少(X=1000..10000)。这将允许我查询各个分区,即使我愿意也可以并行查询。但是(假设用户是随机 UUID)对于 100M 用户,它将导致每行数十或数十万列。在一个请求中读取一个这样的行是一个好主意吗?我敢肯定,这应该会给 Cassandra 带来一些内存压力。所以也许分组阅读它们(例如,Y = 10..100)会更好?
我意识到我想要做的并不是 Cassandra 做得好的事情——以块的形式读取“所有”或大的 CF 数据子集,这些数据可以预先计算(如令牌范围或分区键),以便从不同的主机并行获取。但我试图找到一种对这种用例最有效的模式。
顺便说一句,像“select * from ... where TOKEN(user)>X and TOKEN(user)