Cassandra 文档

版本

您正在查看预发布版本的文档。

评估和优化数据模型

创建物理模型后,您需要采取一些步骤来评估和优化表设计,以确保最佳性能。

计算分区大小

首先要查看的是您的表是否会包含过大的分区,或者换句话说,分区是否过宽。分区大小由存储在分区中的单元格(值)数量来衡量。Cassandra 的硬性限制是每个分区 20 亿个单元格,但您可能会在达到此限制之前遇到性能问题。

要计算分区的大小,请使用以下公式

\[N_v = N_r (N_c - N_{pk} - N_s) + N_s\]

分区中的值(或单元格)数量 (Nv) 等于静态列数量 (Ns) 加上行数 (Nr) 与每行值数量的乘积。每行值的数量定义为列数 (Nc) 减去主键列数 (Npk) 和静态列数 (Ns)。

列数往往比较稳定,尽管可以在运行时更改表。因此,分区大小的主要驱动因素是分区中的行数。这是确定分区是否有可能变得过大的一个关键因素。20 亿个值听起来很多,但在每毫秒测量数十或数百个值的传感器系统中,值的数量会迅速增加。

让我们看一下其中一个表来分析分区大小。由于它具有一个酒店一个分区的宽分区设计,请查看 available_rooms_by_hotel_date 表。该表共有四列 (Nc = 4),包括三列主键 (Npk = 3) 和没有静态列 (Ns = 0)。将这些值代入公式,结果为

\[N_v = N_r (4 - 3 - 0) + 0 = 1N_r\]

因此,该表的数值数量等于行数。您仍然需要确定行数。为此,请根据应用程序设计进行估计。该表为每个酒店的每个房间的每个晚上存储一条记录。假设系统将用于同时存储两年的库存,并且系统中有 5,000 家酒店,每家酒店平均有 100 个房间。

由于每个酒店都有一个分区,因此每个分区的估计行数如下

\[N_r = 100 个房间/酒店 \times 730 天 = 73,000 行\]

每个分区的行数相对较少,不会造成太大问题,但如果您开始存储更多日期的库存,或者没有使用 TTL 很好地管理库存大小,您可能会开始遇到问题。您可能仍然希望考虑将这个大型分区分解,您将在稍后看到如何做到这一点。

在执行大小计算时,很容易假设行数等变量的名义值或平均值。也考虑计算最坏情况,因为这些类型的预测在成功的系统中往往会成为现实。

计算磁盘上的大小

除了计算分区的大小之外,估计您计划在集群中存储的每个表所需的磁盘空间也是一个好主意。为了确定大小,请使用以下公式来确定分区 St 的大小

\[S_t = \displaystyle\sum_i sizeOf\big (c_{k_i}\big) + \displaystyle\sum_j sizeOf\big(c_{s_j}\big) + N_r\times \bigg(\displaystyle\sum_k sizeOf\big(c_{r_k}\big) + \displaystyle\sum_l sizeOf\big(c_{c_l}\big)\bigg) +\]
\[N_v\times sizeOf\big(t_{avg}\big)\]

这比之前的公式复杂一些,但让我们一次分解一部分。首先让我们看一下符号

  • 在这个公式中,ck 指的是分区键列,cs 指的是静态列,cr 指的是普通列,cc 指的是聚类列。

  • 术语 tavg 指的是每个单元格存储的元数据平均字节数,例如时间戳。通常使用 8 字节的估计值。

  • 您会从之前的计算中识别出行数 Nr 和值数 Nv

  • sizeOf() 函数指的是每个引用的列的 CQL 数据类型的字节大小。

第一项要求您对分区键列的大小求和。对于此示例,available_rooms_by_hotel_date 表有一列分区键,即 hotel_id,其类型为 text。假设酒店标识符是简单的 5 个字符代码,则您有一个 5 字节的值,因此分区键列大小的总和为 5 字节。

第二项要求您对静态列的大小求和。此表没有静态列,因此大小为 0 字节。

第三项是最复杂的,也是有充分理由的——它正在计算分区中单元格的大小。对聚类列和普通列的大小求和。两个聚类列是 date,它是 4 字节,以及 room_number,它是 2 字节的短整型,总共 6 字节。只有一列普通列,即布尔值 is_available,它的大小为 1 字节。将普通列大小(1 字节)与聚类列大小(6 字节)相加,总共为 7 字节。为了完成该项,将此值乘以行数 (73,000),得到 511,000 字节 (0.51 MB) 的结果。

第四项只是计算 Cassandra 为每个单元格存储的元数据。在 Cassandra 3.0 及更高版本使用的存储格式中,给定单元格的元数据量会根据存储的数据类型以及是否为单个单元格指定了自定义时间戳或 TTL 值而有所不同。对于此表,请重新使用之前的计算中的值数量 (73,000) 并乘以 8,得到 0.58 MB。

将这些项加在一起,您将得到最终估计

\[分区大小 = 16 字节 + 0 字节 + 0.51 MB + 0.58 MB = 1.1 MB\]

此公式是对磁盘上分区实际大小的近似值,但足够准确,非常有用。请记住,分区必须能够容纳在单个节点上,因此看起来表设计不会给磁盘存储带来很大压力。

Cassandra 的存储引擎在 3.0 版本中重新实现,包括 SSTable 文件的新格式。之前的格式为每个单元格的记录存储了聚类列的单独副本。较新的格式消除了这种重复,从而减少了存储数据的规模并简化了计算该规模的公式。

还要记住,此估计只计算了数据的单个副本。您需要将此处获得的值乘以分区数量和键空间复制策略指定的副本数量,才能确定每个表所需的总容量。这在您规划集群时会派上用场。

分解大型分区

如前所述,目标是设计能够提供您需要的数据的表,这些表使用触及单个分区的查询,或者在无法做到这一点的情况下,使用尽可能少的分区。但是,如示例所示,设计接近 Cassandra 内置限制的宽分区式表是完全可能的。对表执行大小分析可能会发现潜在过大的分区,无论是值数量、磁盘大小还是两者兼而有之。

分割大型分区的技术很简单:在分区键中添加一列。在大多数情况下,将现有列之一移到分区键中就足够了。另一种选择是向表中添加一列作为分片键,但这需要额外的应用程序逻辑。

继续以可用房间示例为例,如果在 `available_rooms_by_hotel_date` 表的分区键中添加 `date` 列,则每个分区将代表特定酒店在特定日期的房间可用情况。这将肯定会产生明显更小的分区,甚至可能太小,因为连续日期的数据可能位于不同的节点上。

另一种称为**分桶**的技术通常用于将数据分成中等大小的分区。例如,您可以通过在分区键中添加 `month` 列(可能以整数表示)来对 `available_rooms_by_hotel_date` 表进行分桶。下图显示了与原始设计的比较。虽然 `month` 列部分重复了 `date`,但它提供了一种很好的方式将相关数据分组到不会变得过大的分区中。

image

如果您确实强烈希望保留宽分区设计,您可以改为将 `room_id` 添加到分区键中,以便每个分区代表所有日期的房间可用情况。由于没有发现涉及搜索特定房间可用性的查询,因此第一种或第二种设计方法最适合应用程序需求。

资料摘自 Cassandra, The Definitive Guide。由 O’Reilly Media, Inc. 出版。版权所有 © 2020 Jeff Carpenter, Eben Hewitt。保留所有权利。经许可使用。