Cassandra 主键选择限制分区增长
Cassandra Primary Key Selection to Bound Partition Growth
我们目前正在测试 Cassanda 作为大量通信事件元数据的数据库。由于大多数查询仅限于单个客户,因此按客户 ID 进行分片是有意义的。然而,这意味着分区将随着时间的推移无限增长。我正在努力想出一个看起来足够干净的解决方案。
第一个想法是使用客户 ID 和某个时间间隔的组合键。是否有其他选择,可能会更好并且更有机地增长?
由于我们希望尽可能少地读取分区,所以我想简单地使用年份来为每个分区的每个客户设置数据上限。但是,如果我没记错的话,这会使数据分布不均。这可以通过移动到几个月甚至 weeks/days 来解决吗?
我确信这是一个经常出现的问题,我很想听听人们提出的各种解决方案。
编辑:为了在查询类型上更加清晰,他们将计算每个客户在大时间片上的聚合。理想情况下,我们只会有这个:
主键((customer_id),时间戳)
但是,正如我所提到的,这将导致多年来每个分区的无限增长。
好吧,一个分区可以容纳大量的行,但是如果您担心多年来的体积,您可以借鉴散列 tables 的想法。当多个值散列为一个值时,多余的值将存储为溢出链表。
我们可以将相同的想法扩展到分区。当一个分区用于大批量客户时 "fills up",我们将额外的分区添加到列表中。
因此您可以这样定义 table:
CREATE TABLE events (
cust_id int,
bucket int,
ts int,
overflow list<int> static,
PRIMARY KEY ((cust_id, bucket), ts));
对于大多数客户,您只需将存储桶设置为零并使用单个分区。但如果零分区太大,则向静态列表添加 1 以指示您现在也在存储桶 1 中存储数据。然后您可以根据需要向列表添加更多分区。
例如:
INSERT INTO events (cust_id, bucket, ts) VALUES (123, 0, 1);
INSERT INTO events (cust_id, bucket, ts) VALUES (123, 0, 2);
SELECT * from events;
cust_id | bucket | ts | overflow
---------+--------+----+----------
123 | 0 | 1 | null
123 | 0 | 2 | null
现在假设您想开始为该客户使用第二个分区,只需将其添加到静态列表即可:
UPDATE events SET overflow = overflow + [1] WHERE cust_id=123 and bucket=0;
INSERT INTO events (cust_id, bucket, ts) VALUES (123, 1, 3);
INSERT INTO events (cust_id, bucket, ts) VALUES (123, 1, 4);
所以要检查客户是否正在使用任何溢出桶分区:
SELECT overflow FROM events WHERE cust_id=123 and bucket=0 limit 1;
overflow
----------
[1]
现在您可以对分区进行范围查询:
SELECT * FROM events WHERE cust_id=123 and bucket IN(0,1) AND ts>1 and ts<4;
cust_id | bucket | ts | overflow
---------+--------+----+----------
123 | 0 | 2 | [1]
123 | 1 | 3 | null
您可以将 "bucket" 定义为您想要的任何含义,例如年份或其他。请注意,溢出列表被定义为静态的,因此每个分区只存储一次,而不是每个事件行。
可能更传统的方法是按 cust_id 和年份进行分区,但是您需要以某种方式知道开始年份和结束年份才能进行查询。使用溢出方法,第一个存储桶是主存储桶并且具有标准的已知值,例如 0 用于读取。但缺点是您需要进行读取才能知道要写入哪个存储桶,但如果每个客户在通信会话期间生成大量事件,那么开销可能不会太多。
我们目前正在测试 Cassanda 作为大量通信事件元数据的数据库。由于大多数查询仅限于单个客户,因此按客户 ID 进行分片是有意义的。然而,这意味着分区将随着时间的推移无限增长。我正在努力想出一个看起来足够干净的解决方案。
第一个想法是使用客户 ID 和某个时间间隔的组合键。是否有其他选择,可能会更好并且更有机地增长?
由于我们希望尽可能少地读取分区,所以我想简单地使用年份来为每个分区的每个客户设置数据上限。但是,如果我没记错的话,这会使数据分布不均。这可以通过移动到几个月甚至 weeks/days 来解决吗?
我确信这是一个经常出现的问题,我很想听听人们提出的各种解决方案。
编辑:为了在查询类型上更加清晰,他们将计算每个客户在大时间片上的聚合。理想情况下,我们只会有这个:
主键((customer_id),时间戳)
但是,正如我所提到的,这将导致多年来每个分区的无限增长。
好吧,一个分区可以容纳大量的行,但是如果您担心多年来的体积,您可以借鉴散列 tables 的想法。当多个值散列为一个值时,多余的值将存储为溢出链表。
我们可以将相同的想法扩展到分区。当一个分区用于大批量客户时 "fills up",我们将额外的分区添加到列表中。
因此您可以这样定义 table:
CREATE TABLE events (
cust_id int,
bucket int,
ts int,
overflow list<int> static,
PRIMARY KEY ((cust_id, bucket), ts));
对于大多数客户,您只需将存储桶设置为零并使用单个分区。但如果零分区太大,则向静态列表添加 1 以指示您现在也在存储桶 1 中存储数据。然后您可以根据需要向列表添加更多分区。
例如:
INSERT INTO events (cust_id, bucket, ts) VALUES (123, 0, 1);
INSERT INTO events (cust_id, bucket, ts) VALUES (123, 0, 2);
SELECT * from events;
cust_id | bucket | ts | overflow
---------+--------+----+----------
123 | 0 | 1 | null
123 | 0 | 2 | null
现在假设您想开始为该客户使用第二个分区,只需将其添加到静态列表即可:
UPDATE events SET overflow = overflow + [1] WHERE cust_id=123 and bucket=0;
INSERT INTO events (cust_id, bucket, ts) VALUES (123, 1, 3);
INSERT INTO events (cust_id, bucket, ts) VALUES (123, 1, 4);
所以要检查客户是否正在使用任何溢出桶分区:
SELECT overflow FROM events WHERE cust_id=123 and bucket=0 limit 1;
overflow
----------
[1]
现在您可以对分区进行范围查询:
SELECT * FROM events WHERE cust_id=123 and bucket IN(0,1) AND ts>1 and ts<4;
cust_id | bucket | ts | overflow
---------+--------+----+----------
123 | 0 | 2 | [1]
123 | 1 | 3 | null
您可以将 "bucket" 定义为您想要的任何含义,例如年份或其他。请注意,溢出列表被定义为静态的,因此每个分区只存储一次,而不是每个事件行。
可能更传统的方法是按 cust_id 和年份进行分区,但是您需要以某种方式知道开始年份和结束年份才能进行查询。使用溢出方法,第一个存储桶是主存储桶并且具有标准的已知值,例如 0 用于读取。但缺点是您需要进行读取才能知道要写入哪个存储桶,但如果每个客户在通信会话期间生成大量事件,那么开销可能不会太多。