使用单行分区的 Cassandra table 是一种不好的做法吗?

Is it a bad practice to have a Cassandra table with partitions of a single row?

假设我有一个这样的table

CREATE TABLE request(
  transaction_id text,
  request_date timestamp,
  data text, 
  PRIMARY KEY (transaction_id)
);

transaction_id 是唯一的,据我所知这个 table 中的每个分区只有一行 ,我不确定如果这种情况导致 OS 出现性能问题,可能是因为 Cassandra 为每个分区创建了一个文件,导致其托管需要管理大量文件 OS,请注意,我不确定 Cassandra 如何创建它的 tables.

文件

在这种情况下,我可以通过 transaction_id 之类的

找到一个请求

select data from request where transaction_id = 'abc';

如果之前的假设是正确的,下一个可能是另一种方法?

CREATE TABLE request( 
  the_date date, 
  transaction_id text, 
  request_date timestamp, 
  data text, 
  PRIMARY KEY ((the_date), transaction_id)
);

字段 the_date 每天都会更改,因此 table 中的分区会每天创建。

在这种情况下,我必须始终向客户端提供 the_date 数据 可用 以便我可以找到请求使用下一个查询

select data from request where the_date = '2020-09-23' and transaction_id = 'abc';

预先感谢您的帮助!

让我先不讨论不同类型的键,但让我提一下并简要解释一下您在问题中使用的两个键。

主键

一行必须有一个唯一的主键(它将行标识为关于相等性的内容)。主键可以是列的集合(如第二个示例中的 (the_date), transaction_id)或只是一个列(如第一个示例中的 transaction_id)。尽管如此,如前所述,重要的部分是对于一行,主键必须是唯一的以标识该行。

分区键

分区键实际上是根据主键确定的。你可以有复合分区键(你在第二个例子中使用了语法,强制 (the_date) 成为分区键,这实际上是没有必要的,因为它默认是主键的第一列).

Cassandra 使用(组合的)分区键值的哈希值来确定数据存储在哪个节点上(或在请求数据时从中检索)。

所以你的问题的答案是,使用 transaction_id 作为主键和分区键是完全可以的。这不是不好的做法,如果您的数据中有一个唯一标识符可以存储在一行中并满足您对请求的需求,这或多或少是很常见的做法。

更多信息:

Cassandra 不会为每个分区创建单独的文件。一个 SSTable 文件可能包含多个分区。仅包含一行的分区通常称为“瘦行”——它们不是很糟糕,但可能会导致一些性能问题:

  • 要访问此类分区,您仍然需要读取一个包含压缩数据的块(默认情况下为 64Kb),需要解压缩才能读取该数据。如果您真正进行随机访问,则此类块将从文件缓存中丢弃,需要从磁盘中 re-read 。在这种情况下,减小块大小可能会有用
  • 如果每个节点每个 table 有很多这样的分区 - 这可能会大大增加布隆过滤器的大小,因为每个分区都有一个单独的条目。我看到一些客户仅仅因为瘦分区就为 bloom filter 分配了数十 GB 的内存

所以这实际上取决于数据量、访问模式等。它可能是好是坏,取决于这些因素。

如果您有可用的日期,并且想将其用作部分分区键 - 这也可能不可取,因为如果您在那天写入和读取大量数据,那么只有一些节点会处理该负载- 这是 so-called“热分区”。

当您从数据推断分区键时,您可以实施 so-called 分桶。但这将取决于可用的数据。例如,如果您将日期 + 事务 ID 作为字符串,则可以将分区键创建为日期 + 该字符串的第一个字符 - 在这种情况下,您每天将有 N 个分区键,它们分布在节点之间,从而消除了热分区问题。

查看关于该主题的corresponding best practices doc from DataStax