【问题标题】:get_range_slices and CQL query handling, need for ALLOW FILTERINGget_range_slices 和 CQL 查询处理,需要 ALLOW FILTERING
【发布时间】:2013-10-24 21:35:22
【问题描述】:

我有一个以下 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 组合的行。

---更新----

感谢所有 cmets。我一直在对此进行更多研究,但仍有一些我不完全理解的东西。

在给定的结构中,我试图从我的数据集中获得一个矩形区域(假设所有行都具有相同的列)。其中矩形的顶部和底部由令牌范围(范围)确定,左侧/右侧定义为列范围(切片)。因此,这应该自然地转换为 get_range_slices 请求。我的理解(如果我错了,请纠正我)CQL 要求我放置 ALLOW FILTERING 子句的原因是因为会有不包含我要查找的列的行,因此必须跳过它们。而且由于没有人知道在找到符合我的标准(在给定范围内)的行之前是否必须跳过每隔一行或前一百万行 - 这就是导致不可预测的延迟甚至可能超时的原因。我对吗?我试图编写一个执行相同类型查询但使用低级 Astyanax API 的测试(在同一张表上,我必须读取使用 CQL 生成的数据,结果非常简单)并且这个测试确实有效- 除了它返回没有列的键,其中行不包含我要求的列切片。当然,我必须根据起始令牌和限制来实现某种简单的分页,以小块获取数据。

现在我想知道 - 再次考虑到我需要处理数以千万计的用户:部分“旋转”该表并以如下方式组织它会更好:

行键:domain_id + app_id + 分区号(类似于 hash(user) mod X) 聚类键:列分区号(类似于 hash(user) >> 16 mod Y) + user

对于“列分区号”...我不确定它是否真的需要。我假设如果我使用这个模型,每个域 + 应用程序组合的行数会相对较少(X=1000..10000)。这将允许我查询各个分区,即使我愿意也可以并行查询。但是(假设用户是随机 UUID)对于 100M 用户,这将导致每行数十或数十万列。在一个请求中读取这样的一行是个好主意吗?我敢肯定,这应该会给 Cassandra 带来一些内存压力。所以也许分组阅读它们(比如,Y = 10..100)会更好?

我意识到我正在尝试做的并不是 Cassandra 做得好的事情 - 以块的形式读取“全部”或大型 CF 数据子集,这些数据可以预先计算(如令牌范围或分区键)以从不同的并行获取主机。但我正在尝试找到一种对此类用例最有效的模式。

顺便说一句,像“select * from ... where TOKEN(user)>X and TOKEN(user)”这样的查询

【问题讨论】:

    标签: cassandra cql cql3


    【解决方案1】:

    简答

    此警告意味着 Cassandra 必须读取非索引数据并过滤掉不满足条件的行。如果您在查询末尾添加ALLOW FILTERING,它会起作用,但是它会扫描大量数据:

    SELECT props FROM test_table 
    WHERE app_id='myapp1' 
    AND domain_id='mydomain1' 
    AND TOKEN(user) > -9223372036854775808 
    AND TOKEN(user) < 9223372036854775807
    ALLOW FILTERING;
    

    更长的解释

    在您的示例中,主键由两部分组成:user 用作分区键,&lt;app_id, domain_id&gt; 构成剩余部分。不同用户的行分布在整个集群中,每个节点负责特定范围的令牌环。

    单个节点上的行按分区键的哈希排序(在您的示例中为token(user))。单个用户的不同行存储在单个节点上,按&lt;app_id, domain_id&gt; 元组排序。

    所以,主键形成了一个树状结构。分区键增加了一层层次结构,主键的每个剩余字段都增加了一层。默认情况下,Cassandra 只处理从树的连续范围(或者如果使用 key in (...) 构造,则返回多个范围)中返回所有行的查询。如果 Cassandra 应该过滤掉某些行,则必须指定 ALLOW FILTERING

    需要ALLOW FILTERING的查询示例:

    SELECT * FROM test_table 
    WHERE user = 'user1'; 
    //OK, returns all rows for a single partition key
    
    SELECT * FROM test_table 
    WHERE TOKEN(user) > -9223372036854775808 
    AND TOKEN(user) < 9223372036854775807; 
    //OK, returns all rows for a continuos range of the token ring
    
    SELECT * FROM test_table 
    WHERE user = 'user1'
    AND app_id='myapp1'; 
    //OK, the rows for specific user/app combination 
    //are stored together, sorted by domain_id field
    
    SELECT * FROM test_table 
    WHERE user = 'user1'
    AND app_id > 'abc' AND app_id < 'xyz'; 
    //OK, since rows for a single user are sorted by app
    

    确实需要ALLOW FILTERING的查询示例:

    SELECT props FROM test_table 
    WHERE app_id='myapp1';
    //Must scan all the cluster for rows, 
    //but return only those with specific app_id
    
    SELECT props FROM test_table 
    WHERE user='user1'
    AND domain_id='mydomain1';
    //Must scan all rows having user='user1' (all app_ids), 
    //but return only those having specific domain
    
    SELECT props FROM test_table 
    WHERE user='user1'
    AND app_id > 'abc' AND app_id < 'xyz'
    AND domain_id='mydomain1';
    //Must scan the range of rows satisfying <user, app_id> condition,
    //but return only those having specific domain
    

    怎么办?

    在 Cassandra 中,无法在主键部分创建二级索引。选项很少,各有利弊:

    • 添加一个具有主键((app_id), domain_id, user) 的单独表,并在两个表中复制必要的数据。它将允许您查询特定app_id&lt;app_id, domain_id&gt; 组合的必要数据。如果您需要查询特定域和所有应用程序 - 第三个表是必要的。这种方法称为物化视图
    • 使用某种并行处理(hadoop、spark 等)为所有应用程序/域组合执行必要的计算。由于 Cassandra 无论如何都需要读取所有数据,因此与一对可能不会有太大区别。如果可能会缓存其他对的结果以供以后使用,则可能会节省一些时间。
    • 如果查询性能可以满足您的需求,请使用ALLOW FILTERING。数以万计的分区键对于 Cassandra 来说可能并不算多。

    【讨论】:

      【解决方案2】:

      假设您正在使用 Murmur3Partitioner(这是正确的选择),您不想在行键上运行范围查询。该键被散列以确定哪个节点保存该行,因此不按排序顺序存储。因此,执行这种范围查询需要全面扫描。

      如果您想执行此查询,您应该存储一些已知值作为行键的标记,这样您就可以查询相等而不是范围。从您的数据看来,app_id 或 domain_id 都是不错的选择,因为听起来您在执行查询时总是知道这些值。

      【讨论】:

      • 我只是想澄清一下,最终这正是我们的想法——从表中加载所有数据。但是要以大约相等大小的块加载它 - 这是通过扫描整个令牌范围的部分来实现的。 IE。要以 64 个块加载数据,我将运行 64 个这样的查询,每个查询都有自己的 2^64 长令牌值范围(Murmur3 分区器)。但是通过添加另外两个条件,我想从每一行中选择一个切片。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-03-16
      • 2017-05-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-10-07
      • 2022-07-07
      相关资源
      最近更新 更多