Cassandra - 二级索引和查询性能
Cassandra - secondary index and query performance
我的 table 架构是:
A)
CREATE TABLE friend_list (
userId uuid,
friendId uuid,
accepted boolean,
ts_accepted timestamp,
PRIMARY KEY ((userId ,accepted), ts_accepted)
) with clustering order by (ts_accepted desc);
在这里我可以执行如下查询:
1. SELECT * FROM friend_list WHERE userId="---" AND accepted=true;
2. SELECT * FROM friend_list WHERE userId="---" AND accepted=false;
3. SELECT * FROM friend_list WHERE userId="---" AND accepted IN (true,false);
但是第三个查询涉及更多读取,所以我尝试像这样更改模式:
B)
CREATE TABLE friend_list (
userId uuid,
friendId uuid,
accepted boolean,
ts_accepted timestamp,
PRIMARY KEY (userId , ts_accepted)
) with clustering order by (ts_accepted desc);
CREATE INDEX ON friend_list (accepted);
对于这种类型 B 模式,第一个和第二个查询有效,但我可以将第三个查询简化为:
3. SELECT * FROM friend_list WHERE userId="---";
我相信第二个模式为第三个查询提供了更好的性能,因为它不会对每一行进行条件检查。
Cassandra 专家...请建议我哪个是实现 this.A 或 B 的最佳模式。
首先,您是否知道您的第二个架构与第一个架构完全不同?在第一个中 'accepted' 字段是键的一部分,但在第二个中根本不是!您没有相同的唯一约束,您应该检查它是否不是您的模型的问题。
其次,如果您只想不必为每个请求包含 'acceptation' 字段,您有两种可能性:
1 - 您可以使用 'acceptation' 作为聚类列:
PRIMARY KEY ((userId), accepted, ts_accepted)
这样你的第三个请求可以是:
SELECT * FROM friend_list WHERE userId="---";
而且你会更有效地得到同样的结果。
但这种方法有一个问题,它会创建更大的分区,这不是获得良好性能的最佳选择。
2 - 创建两个单独的 tables
这种方法更适合 Cassandra 精神。使用 Cassandra,如果可以提高请求效率,复制数据并不罕见。
所以在你的情况下,你会为第一个 table 和第一个和第二个请求保留你的第一个模式,
并且您将使用相同的数据创建另一个 table,但架构略有不同,如果 'accepted' 不需要成为主键的一部分(如您为你的第二个模式做了),或者像这样的主键:
PRIMARY KEY ((userId), accepted, ts_accepted)
如果可能的话,我肯定更喜欢第二个 table 的二级索引,因为接受的列具有较低的基数 (2),因此非常适合二级索引。
编辑:
您还在主键中使用了时间戳。请注意,如果您可以让同一用户在此 table 中创建两行,则可能会出现问题。因为时间戳不保证唯一性:如果两行在同一毫秒内创建会怎样?
您可能应该使用 TimeUUID。这种在 Cassandra 中非常常用的类型通过组合时间戳和 UUID 来保证唯一性。
此外,主键中的时间戳可以在 Cassandra 节点中创建临时热点,最好避免。
我的 table 架构是:
A)
CREATE TABLE friend_list (
userId uuid,
friendId uuid,
accepted boolean,
ts_accepted timestamp,
PRIMARY KEY ((userId ,accepted), ts_accepted)
) with clustering order by (ts_accepted desc);
在这里我可以执行如下查询:
1. SELECT * FROM friend_list WHERE userId="---" AND accepted=true;
2. SELECT * FROM friend_list WHERE userId="---" AND accepted=false;
3. SELECT * FROM friend_list WHERE userId="---" AND accepted IN (true,false);
但是第三个查询涉及更多读取,所以我尝试像这样更改模式:
B)
CREATE TABLE friend_list (
userId uuid,
friendId uuid,
accepted boolean,
ts_accepted timestamp,
PRIMARY KEY (userId , ts_accepted)
) with clustering order by (ts_accepted desc);
CREATE INDEX ON friend_list (accepted);
对于这种类型 B 模式,第一个和第二个查询有效,但我可以将第三个查询简化为:
3. SELECT * FROM friend_list WHERE userId="---";
我相信第二个模式为第三个查询提供了更好的性能,因为它不会对每一行进行条件检查。
Cassandra 专家...请建议我哪个是实现 this.A 或 B 的最佳模式。
首先,您是否知道您的第二个架构与第一个架构完全不同?在第一个中 'accepted' 字段是键的一部分,但在第二个中根本不是!您没有相同的唯一约束,您应该检查它是否不是您的模型的问题。
其次,如果您只想不必为每个请求包含 'acceptation' 字段,您有两种可能性:
1 - 您可以使用 'acceptation' 作为聚类列:
PRIMARY KEY ((userId), accepted, ts_accepted)
这样你的第三个请求可以是:
SELECT * FROM friend_list WHERE userId="---";
而且你会更有效地得到同样的结果。
但这种方法有一个问题,它会创建更大的分区,这不是获得良好性能的最佳选择。
2 - 创建两个单独的 tables
这种方法更适合 Cassandra 精神。使用 Cassandra,如果可以提高请求效率,复制数据并不罕见。
所以在你的情况下,你会为第一个 table 和第一个和第二个请求保留你的第一个模式,
并且您将使用相同的数据创建另一个 table,但架构略有不同,如果 'accepted' 不需要成为主键的一部分(如您为你的第二个模式做了),或者像这样的主键:
PRIMARY KEY ((userId), accepted, ts_accepted)
如果可能的话,我肯定更喜欢第二个 table 的二级索引,因为接受的列具有较低的基数 (2),因此非常适合二级索引。
编辑:
您还在主键中使用了时间戳。请注意,如果您可以让同一用户在此 table 中创建两行,则可能会出现问题。因为时间戳不保证唯一性:如果两行在同一毫秒内创建会怎样?
您可能应该使用 TimeUUID。这种在 Cassandra 中非常常用的类型通过组合时间戳和 UUID 来保证唯一性。
此外,主键中的时间戳可以在 Cassandra 节点中创建临时热点,最好避免。