Apache Cassandra 是一个数据库。但它不仅仅是一个普通的数据库;它是一个复制数据库,专为可扩展性、高可用性、低延迟和高性能而设计和调整。Cassandra 可以帮助您的数据在区域性中断、硬件故障以及许多管理员认为过多的数据量的情况下幸存下来。
深入了解数据分区使您能够实现卓越的 Cassandra 集群设计、性能和可扩展性。在本文中,我将探讨如何定义分区以及 Cassandra 如何使用它们,以及您应该了解的最关键的最佳实践和已知问题。
为了说明背景:分区是数据块,作为关键数据库相关功能的原子单元,例如数据分发、复制和索引。分布式数据系统通常将传入数据分发到这些分区中,使用简单的数学函数(例如标识或哈希)执行分区,并使用“分区键”按分区对数据进行分组。例如,考虑服务器日志作为传入数据到达的情况。使用“标识”分区函数和每个日志的时间戳(四舍五入到小时值)作为分区键,我们可以对这些数据进行分区,以便每个分区保存一小时的日志。
Cassandra 中的数据分区
Cassandra 作为分布式系统运行,并遵守上述数据分区原则。对于 Cassandra,数据分区依赖于在集群级别配置的算法以及在表级别配置的分区键。

Cassandra 查询语言 (CQL) 使用熟悉的 SQL 表、行和列术语。在上图的示例中,表配置在其主键中包含分区键,格式为:主键 = 分区键 + [聚簇列]。
Cassandra 中的主键表示唯一的数据分区和分区内的数据排列。数据排列信息由可选的聚簇列提供。每个唯一的分区键代表一组在服务器中管理的表行,以及管理其副本的所有服务器。
在 CQL 中定义主键
以下四个示例演示了如何在 CQL 语法中表示主键。这些定义产生的一组行通常被认为是一个分区。
定义 1(分区键:log_hour,聚簇列:无)
CREATE TABLE server_logs(
log_hour timestamp PRIMARYKEY,
log_level text,
message text,
server text
)
在这里,所有共享 log_hour 的行都进入同一个分区。
定义 2(分区键:log_hour,聚簇列:log_level)
CREATE TABLE server_logs(
log_hour timestamp,
log_level text,
message text,
server text,
PRIMARY KEY (log_hour, log_level)
)
此定义使用与定义 1 相同的分区键,但此处每个分区中的所有行都按 log_level 升序排列。
定义 3(分区键:log_hour, server,聚簇列:无)
CREATE TABLE server_logs(
log_hour timestamp,
log_level text,
message text,
server text,
PRIMARY KEY ((log_hour, server))
)
在此定义中,所有行都共享每个不同 server 的 log_hour 作为一个分区。
定义 4(分区键:log_hour, server,聚簇列:log_level)
CREATE TABLE server_logs(
log_hour timestamp,
log_level text,
message text,
server text,
PRIMARY KEY ((log_hour, server),log_level)
)WITH CLUSTERING ORDER BY (column3 DESC);
此定义使用与定义 3 相同的分区,但按 log_level 降序排列分区内的行。
Cassandra 如何使用分区键
Cassandra 依靠分区键来确定将数据存储在哪个节点上,以及在需要时在哪里查找数据。Cassandra 通过查看表中的分区键,并使用令牌(范围为 -2^63 到 +2^63-1 的长整型值)进行数据分发和索引来执行这些读取和写入操作。这些令牌通过分区器映射到分区键,分区器应用分区函数将任何分区键转换为令牌。通过这种令牌机制,Cassandra 集群的每个节点都拥有一组数据分区。然后,分区键在每个节点上启用数据索引。

具有三个节点和基于令牌所有权的 Cassandra 集群。这是一个简化的表示形式:实际实现使用 Vnodes。
数据分区对 Cassandra 集群的影响
仔细的分区键设计对于为用例实现理想的分区大小至关重要。正确的设计允许均匀的数据分发和强大的 I/O 性能。分区大小对 Cassandra 集群有几个影响,您需要注意:
- 读取性能——为了在磁盘上的 SSTables 文件中查找分区,Cassandra 使用包括缓存、索引和索引摘要的数据结构。过大的分区会降低维护这些数据结构的效率,并因此对性能产生负面影响。Cassandra 版本在这方面取得了进展:特别是,Cassandra 引擎的 3.6 及更高版本引入了存储改进,为大型分区提供更好的性能,并提高了对内存问题和崩溃的弹性。
- 内存使用——大型分区给 JVM 堆带来更大的压力,增加其大小,同时也降低了垃圾回收机制的效率。
- Cassandra 修复——大型分区使 Cassandra 更难以执行其修复维护操作,这些操作通过比较跨副本的数据来保持数据一致性。
- 墓碑回收——不像听起来那么可怕,Cassandra 使用称为“墓碑”的唯一标记来标记要删除的数据。如果没有适当的数据删除模式和压缩策略,大型分区可能会使删除过程更加困难。
虽然这些影响可能使人想要简单地设计产生特别小分区的分区键,但数据访问模式对理想的分区大小也具有很大的影响(有关更多信息,请阅读这篇深入的 Cassandra 数据建模指南)。数据访问模式可以定义为如何查询表,包括表的所有 select 查询。理想情况下,CQL select 查询的 where 子句中应该只有一个分区键——也就是说,当查询可以从单个分区而不是许多较小的分区获取所需数据时,Cassandra 的效率最高。
分区键设计的最佳实践
遵循分区键设计的最佳实践有助于您获得理想的分区大小。作为经验法则,Cassandra 中的最大分区大小应保持在 100MB 以下。理想情况下,它应该在 10MB 以下。虽然 Cassandra 3.6 及更高版本使更大的分区大小更可行,但必须对每个工作负载执行仔细的测试和基准测试,以确保分区键设计支持所需的集群性能。
具体而言,在任何分区键设计中都应考虑以下最佳实践:
- 分区键的目标必须是在每个分区中容纳理想的数据量,以支持其访问模式的需求。
- 分区键应禁止无界分区:那些可能随着时间推移无限增长的分区。例如,在上面的 server_logs 示例中,使用 server 列作为分区键将创建无界分区,因为服务器日志的数量会持续增加。相反,使用 log_hour 将每个分区限制为一个小时的数据。
- 分区键还应避免创建分区倾斜,即分区增长不均匀,并且有些分区能够随着时间推移无限制地增长。在 server_logs 示例中,在某个服务器生成的日志比其他服务器多得多的情况下使用 server 列将产生分区倾斜。为了避免这种情况,一个有用的技术是从表中引入另一个属性来强制均匀分布,即使有必要创建一个虚拟列来做到这一点。
- 使用以时间元素以及其他属性作为分区键来分区时间序列数据很有帮助。这可以防止无界分区,使访问模式能够在查询特定数据时使用时间属性,并允许进行时间限制的数据删除。上面的每个示例都通过使用 log_hour 时间属性来演示了这一点。
有多种工具可用于帮助测试、分析和监控 Cassandra 分区,以检查所选模式是否高效且有效。通过仔细设计分区键以使其与手头解决方案的数据和需求良好对齐,并遵循最佳实践来优化分区大小,您可以利用数据分区,从而更充分地发挥 Cassandra 部署的可扩展性和性能潜力。
1 条评论