基于二级索引的卡桑德拉过滤器



我们已经使用Cassandra有一段时间了,我们正在尝试得到一个真正优化的表,它将能够快速查询和过滤大约10万行。

我们的模型看起来像这样:

class FailedCDR(Model):  
    uuid = columns.UUID(partition_key=True, primary_key=True)
    num_attempts = columns.Integer(index=True)
    datetime = columns.Integer()

如果我描述这个表,它清楚地表明num_attempts是索引。

CREATE TABLE cdrs.failed_cdrs (
    uuid uuid PRIMARY KEY,
    datetime int,
    num_attempts int
) WITH bloom_filter_fp_chance = 0.01
    AND caching = '{"keys":"ALL", "rows_per_partition":"NONE"}'
    AND comment = ''
    AND compaction = {'class': 'org.apache.cassandra.db.compaction.SizeTieredCompactionStrategy'}
    AND compression = {'sstable_compression': 'org.apache.cassandra.io.compress.LZ4Compressor'}
    AND dclocal_read_repair_chance = 0.1
    AND default_time_to_live = 0
    AND gc_grace_seconds = 864000
    AND max_index_interval = 2048
    AND memtable_flush_period_in_ms = 0
    AND min_index_interval = 128
    AND read_repair_chance = 0.0
    AND speculative_retry = '99.0PERCENTILE';
CREATE INDEX index_failed_cdrs_num_attempts ON cdrs.failed_cdrs (num_attempts);

我们希望能够运行一个类似于这样的过滤器:

failed = FailedCDR.filter(num_attempts__lte=9)

但是发生了:

QueryException: Where clauses require either a "=" or "IN" comparison with either a primary key or indexed field

我们怎样才能完成类似的任务?

如果你想在CQL中做一个范围查询,你需要这个字段是一个集群列。

所以您将希望num_attempts字段是一个集群列。

同样,如果您想要执行单个查询,则需要在同一个分区中查询所有想要查询的行(或者可以使用in子句访问的少量分区)。因为您只有100K行,所以这足够小,可以放在一个分区中。

你可以这样定义你的表:

CREATE TABLE test.failed_cdrs (
    partition int,
    num_attempts int,
    uuid uuid,
    datetime int,
    PRIMARY KEY (partition, num_attempts, uuid));

您将使用分区键常量插入数据,例如1。

INSERT INTO failed_cdrs (uuid, datetime, num_attempts, partition)
    VALUES ( now(), 123, 5, 1);

然后你可以做这样的范围查询:

SELECT * from failed_cdrs where partition=1 and num_attempts >=8;

该方法的缺点是要更改num_attempts的值,需要删除旧行并插入新行,因为不允许更新关键字段。您可以在批处理语句中对其执行删除和插入操作。

在Cassandra 3.0中,一个更好的选择是创建一个实体化视图,将num_attempts作为集群列,在这种情况下,当你在基表中更新num_attempts时,Cassandra会为你处理删除和插入操作。3.0版本目前正在beta测试中。

最新更新