在 Cassandra 中定义分区键
Defining a partition key in Cassandra
我是第一次玩 Cassandra,我觉得我了解基础知识和限制。例如,我正在使用以下模型来存储由主题标签收集的推文。
create table posts
(
id text,
status text,
service text,
hashtag text,
username text,
caption text,
image text,
link text,
repost boolean,
created timestamp,
primary key (hashtag, created)
);
这非常适合我需要的查询类型:
select * from posts where hashtag = 'demo' order by created desc;
但是,如果我理解正确的话,我可以使用单个 'demo' 分区键存储 post 的数量上限,更重要的是,整组 posts 匹配 'demo' 分区键必须与每个副本一起存储。如果我理解正确的话,我可能应该使用更随机或可变的分区键(可能是 post 的 id
),但我不知道使用什么不会改变要求用于查询。
如果我使用 id
作为分区键(例如 PRIMARY KEY (id, created)
)并在 hashtag 列上添加二级索引,我在 运行 查询时会收到以下错误:
ORDER BY with 2ndary indexes is not supported.
我知道要使用 ORDER BY,分区键必须出现在 where 子句中,因此我最初的想法是使用 hashtag。
我是想多了还是有更好的分区键候选者?
您前进的方向将取决于您期望的写入量以及您的集群有多大。
如果你有一个小的用户社区和一个小的集群,那么你可能想多了。一个分区理论上最多可以容纳 20 亿行。这是一个很大的数字,真的会有人想要查看超过几千条的最新推文以获得标签吗?所以你可能有某种清理机制,比如在一段时间后使用 TTL 删除推文,这将释放分区中的 space,使你远低于 20 亿行限制。
如果您不想清理旧推文,但希望将它们保存多年,那么您可能需要使用这样的复合分区键:
primary key ((hashtag, year), created)
这将按标签和年份对推文进行分区,因此您每年每个标签最多可以存储 20 亿条推文。
按主题标签进行分区的好处在于,Cassandra 可以按创建时间戳对标签的推文进行排序,从而可以轻松地通过单个查询检索最新的推文,如您所示。
但是如果你的用户群体很大,那么更重要的问题就是避免热点。如果您只使用主题标签和时间仓(如年份)作为分区键,那么所有读取和写入都将针对该主题标签的少量副本。如果主题标签在给定的一天非常活跃,那么根据您使用的复制因子,您的所有读取和写入操作只会转到一个或两个节点。
如果要分散读写负载,则需要增加主题标签的基数,以便它映射到多个节点。使用 id 作为分区键可以实现这一点,但是这样做就太过分了,因为那时每条推文都会在一个单独的分区中,您将无法进行排序或检索主题标签的最新推文的简便方法。
所以更好的方法是创建单独的容器或桶,如下所示:
primary key ((hashtag, bin), created)
您创建的 bin 数量取决于您的写入负载。假设您决定有 10 个节点可以处理热门标签的写入负载,那么 bin 将是 0 到 9 之间的一个值。
设置 bin 编号的方法有很多种。您可以对 id 取模 10,或者选择 0 到 9 之间的随机数,或者从某些字段组合生成哈希值并对结果取模 10。无论您选择哪种方法,请确保从 0 到 9 的数字出现的可能性相同,以便您的数据在 bin 分区中平均分布。
对于多个容器,检索主题标签的 x 个最新推文并不容易,因为您需要查询所有容器并合并结果。您可以并行地针对主题标签的每个容器异步发出查询,然后在客户端合并结果。或者您可以像这样使用 IN 子句进行单个查询:
select * from posts where hashtag = 'demo' and bin IN (0,1,2,3,4,5,6,7,8,9) AND created > ...
但 Cassandra 不会对单个查询的结果进行排序,因此您必须在客户端进行排序,这比合并单独的有序查询要慢。
现在,在很多情况下,主题标签的体积非常小,因此除非它们变热,否则您可能不想为它们使用十个垃圾箱。如果是这样,您可以在您的应用程序中使其动态化,通常只使用 bin 0,但是当发现标签流行时增加 bin 的数量。您可以在 bin 0 中使用静态列来跟踪主题标签的活动 bin 数量。
您应该避免使用二级索引。它们在 Cassandra 中效率很低。
我是第一次玩 Cassandra,我觉得我了解基础知识和限制。例如,我正在使用以下模型来存储由主题标签收集的推文。
create table posts
(
id text,
status text,
service text,
hashtag text,
username text,
caption text,
image text,
link text,
repost boolean,
created timestamp,
primary key (hashtag, created)
);
这非常适合我需要的查询类型:
select * from posts where hashtag = 'demo' order by created desc;
但是,如果我理解正确的话,我可以使用单个 'demo' 分区键存储 post 的数量上限,更重要的是,整组 posts 匹配 'demo' 分区键必须与每个副本一起存储。如果我理解正确的话,我可能应该使用更随机或可变的分区键(可能是 post 的 id
),但我不知道使用什么不会改变要求用于查询。
如果我使用 id
作为分区键(例如 PRIMARY KEY (id, created)
)并在 hashtag 列上添加二级索引,我在 运行 查询时会收到以下错误:
ORDER BY with 2ndary indexes is not supported.
我知道要使用 ORDER BY,分区键必须出现在 where 子句中,因此我最初的想法是使用 hashtag。
我是想多了还是有更好的分区键候选者?
您前进的方向将取决于您期望的写入量以及您的集群有多大。
如果你有一个小的用户社区和一个小的集群,那么你可能想多了。一个分区理论上最多可以容纳 20 亿行。这是一个很大的数字,真的会有人想要查看超过几千条的最新推文以获得标签吗?所以你可能有某种清理机制,比如在一段时间后使用 TTL 删除推文,这将释放分区中的 space,使你远低于 20 亿行限制。
如果您不想清理旧推文,但希望将它们保存多年,那么您可能需要使用这样的复合分区键:
primary key ((hashtag, year), created)
这将按标签和年份对推文进行分区,因此您每年每个标签最多可以存储 20 亿条推文。
按主题标签进行分区的好处在于,Cassandra 可以按创建时间戳对标签的推文进行排序,从而可以轻松地通过单个查询检索最新的推文,如您所示。
但是如果你的用户群体很大,那么更重要的问题就是避免热点。如果您只使用主题标签和时间仓(如年份)作为分区键,那么所有读取和写入都将针对该主题标签的少量副本。如果主题标签在给定的一天非常活跃,那么根据您使用的复制因子,您的所有读取和写入操作只会转到一个或两个节点。
如果要分散读写负载,则需要增加主题标签的基数,以便它映射到多个节点。使用 id 作为分区键可以实现这一点,但是这样做就太过分了,因为那时每条推文都会在一个单独的分区中,您将无法进行排序或检索主题标签的最新推文的简便方法。
所以更好的方法是创建单独的容器或桶,如下所示:
primary key ((hashtag, bin), created)
您创建的 bin 数量取决于您的写入负载。假设您决定有 10 个节点可以处理热门标签的写入负载,那么 bin 将是 0 到 9 之间的一个值。
设置 bin 编号的方法有很多种。您可以对 id 取模 10,或者选择 0 到 9 之间的随机数,或者从某些字段组合生成哈希值并对结果取模 10。无论您选择哪种方法,请确保从 0 到 9 的数字出现的可能性相同,以便您的数据在 bin 分区中平均分布。
对于多个容器,检索主题标签的 x 个最新推文并不容易,因为您需要查询所有容器并合并结果。您可以并行地针对主题标签的每个容器异步发出查询,然后在客户端合并结果。或者您可以像这样使用 IN 子句进行单个查询:
select * from posts where hashtag = 'demo' and bin IN (0,1,2,3,4,5,6,7,8,9) AND created > ...
但 Cassandra 不会对单个查询的结果进行排序,因此您必须在客户端进行排序,这比合并单独的有序查询要慢。
现在,在很多情况下,主题标签的体积非常小,因此除非它们变热,否则您可能不想为它们使用十个垃圾箱。如果是这样,您可以在您的应用程序中使其动态化,通常只使用 bin 0,但是当发现标签流行时增加 bin 的数量。您可以在 bin 0 中使用静态列来跟踪主题标签的活动 bin 数量。
您应该避免使用二级索引。它们在 Cassandra 中效率很低。