拒绝列表分区
由于访问模式和数据建模,有时会有一些特定的分区是“热点”,会导致 Cassandra 集群不稳定。这通常发生在您的数据模型包含对单个分区的许多更新或插入操作时,导致分区随着时间的推移而变得非常大,进而使其读取和维护变得非常昂贵。
Cassandra 支持“拒绝列表”这些有问题的分区,以便当客户端发出点读取(指定了分区键的 SELECT
语句)或范围读取(SELECT *
等,拉取数据范围)与被阻止的分区键相交时,查询将立即被拒绝,并出现 InvalidQueryException
错误。
如何拒绝列表分区键
system_distributed.denylisted_partitions
表可用于拒绝列表分区。有几种方法可以与这些数据交互并对其进行修改。首先:通过 CQL 直接插入具有以下详细信息的记录
-
键空间名称 (ks_name)
-
表名称 (table_name)
-
分区键 (partition_key)
分区键格式需要与 nodetool getendpoints
所需的格式相同。
以下是几个在键空间 ks
和表 table1
中为主键 Id
上的不同数据类型拒绝列表分区键的示例
-
Id 是一个简单类型 -
INSERT INTO system_distributed.denylisted_partitions (ks_name, table_name, partition_key) VALUES ('ks','table1','1');
-
Id 是一个 blob -
INSERT INTO system_distributed.denylisted_partitions (ks_name, table_name, partition_key) VALUES ('ks','table1','12345f');
-
Id 包含冒号 -
INSERT INTO system_distributed.denylisted_partitions (ks_name, table_name, partition_key) VALUES ('ks','table1','1\:2');
对于复合列分区键 (Key1, Key2)
-
INSERT INTO system_distributed.denylisted_partitions (ks_name, table_name, partition_key) VALUES ('ks', 'table1', 'k11:k21')
特殊注意事项
拒绝列表具有以下特性:您希望将缓存(见下文)和 CQL 数据尽可能地保存在同一个副本集中,这样您的集群中就不会有不同的节点拒绝或允许不同的键。为了最好地实现这一点,拒绝列表更改(添加或删除)的工作流程应始终如下所示
JMX 路径(适用于单个更改的首选方法)
-
使用所需的键调用
denylistKey()
的 JMX 钩子 -
使用
isKeyDenylisted()
双重检查缓存是否已重新加载 -
检查有关无法识别的键空间/表组合、限制或一致性级别的警告。如果您收到有关节点关闭且未命中拒绝列表的 CL 的消息,请恢复关闭的节点,然后在每个节点上使用
loadPartitionDenylist()
触发缓存重新加载
CQL 路径(适用于批量更改的首选方法)
-
通过 CQL 修改拒绝列表分区列表
-
通过 JMX
loadPartitionDenylist()
(见下文)在每个节点上触发拒绝列表缓存重新加载 -
检查有关拒绝列表刷新缺乏可用性的警告。如果节点关闭,请恢复它们,然后转到步骤 2。
由于已知不可用范围切片上的条件会导致启动时出现警报风暴,因此拒绝列表缓存不会在节点启动时加载,除非它能够在 cassandra.yaml
中配置的一致性级别(denylist_consistency_level
)下实现。但是,对 loadPartitionDenylist
的 JMX 调用将加载缓存,无论有多少节点可用。这将使操作员能够在集群状态降级期间控制是否拒绝列表。
拒绝列表分区缓存
Cassandra 在内部维护一个从 system_distributed.denylisted_partitions
加载的拒绝列表分区的堆内缓存。表的这些值将在 conf/cassandra.yaml
文件中指定的 denylist_refresh
(默认值为 600s
,即 10 分钟)后自动重新填充。无效记录(未知键空间、表或键)将被忽略,不会在加载时缓存。
缓存可以通过以下方式刷新
-
在 Cassandra 节点启动期间
-
通过自动堆内缓存刷新机制。注意:这将在查询后异步发生,直到达到
denylist_refresh
时间。 -
通过 JMX 命令:
org.apache.cassandra.service.StorageProxyMBean
调用点中的loadPartitionDenylist
。
缓存大小受以下两个配置属性限制
-
denylist_max_keys_per_table
-
denylist_max_keys_total
在缓存加载时,如果表超过了 denylist_max_keys_per_table
(默认值为 1000)中允许的值,则会向日志打印警告,剩余的键将不会被缓存。类似地,如果超过了允许的总大小,则后续的 ks_name + table_name 组合(按聚类/词典顺序)也将被跳过,并向服务器日志记录警告。
鉴于所需的工作流程是 1)修改,2)重新加载缓存,自动重新加载属性似乎是多余的。它的存在是为了确保如果操作员犯了错误,拒绝列表(或取消拒绝列表)一个键,但忘记重新加载缓存,该意图将在下次缓存重新加载时被捕获。 |
JMX 接口
命令 | 效果 |
---|---|
loadPartitionDenylist() |
从 CQL 表重新加载缓存的拒绝列表 |
getPartitionDenylistLoadAttempts() |
获取缓存重新加载尝试次数 |
getPartitionDenylistLoadSuccesses() |
获取缓存重新加载成功次数 |
setEnablePartitionDenylist(boolean enabled) |
启用或禁用分区拒绝列表功能 |
setEnableDenylistWrites(boolean enabled) |
启用或禁用写入拒绝列表功能 |
setEnableDenylistReads(boolean enabled) |
启用或禁用读取拒绝列表功能 |
setEnableDenylistRangeReads(boolean enabled) |
启用或禁用范围读取拒绝列表功能 |
denylistKey(String keyspace, String table, String partitionKeyAsString) |
将特定的键空间、表和分区键组合添加到拒绝列表 |
removeDenylistKey(String keyspace, String cf, String partitionKeyAsString) |
从拒绝列表中删除特定的键空间、表和分区键组合 |
setDenylistMaxKeysPerTable(int value) |
限制拒绝列表中每个表允许的键数量 |
setDenylistMaxKeysTotal(int value) |
限制系统中允许的拒绝列表键的总数 |
isKeyDenylisted(String keyspace, String table, String partitionKeyAsString) |
指示键空间.表是否拒绝了输入的分区键 |