【问题标题】:Understanding Cassandra's storage overhead了解 Cassandra 的存储开销
【发布时间】:2014-07-20 10:33:31
【问题描述】:

我一直在阅读 Cassandra 文档的 this section 并发现以下内容有点令人费解:

确定列开销:

regular_total_column_size = column_name_size + column_value_size + 15

计数器 - expiring_total_column_size = column_name_size + column_value_size + 23

Cassandra 中的每一列都会产生 15 个字节的开销。由于表中的每一行可以具有不同的列名以及不同的列数,因此为每列存储元数据。对于计数器列和即将到期的列,您应该额外添加 8 个字节(总共 23 个字节)。

我对 CQL3 定义的架构的上述解释方式,例如:

CREATE TABLE mykeyspace.mytable(
  id text,
  report_id text,
  subset_id text,
  report_date timestamp,
  start_date timestamp,
  end_date timestamp,
  subset_descr text,
  x int,
  y double,
  z int,
  PRIMARY KEY (id, report_id, subset_id)
);

是每行将包含列名称的元数据,例如字符串report_date、start_date、end_date 等,以及它们的类型以及数据。但是,我不清楚表中的每一行可以有不同的列名意味着什么。考虑到上面的架构完全是静态的,这对我来说是错误的,也就是说,如果我尝试编写,Cassandra 2.0 肯定会抱怨:

INSERT INTO mykeyspace.mytable (id, report_id , subset_id, x, y, z, w) 
VALUES ( 'asd','qwe','rty',100,1.234,12, 123.123);

Bad Request: Unknown identifier w

现在在我看来,鉴于此表架构,列名是固定的,因此元数据不需要按每一行存储。我猜测文档中的措辞已经过时(与 Ca​​ssandra 1.2 相同),或者我在这里误解了一些核心概念。

谁能澄清一下?底线:我是否需要担心列名的长度?

我们一直在谨慎行事,并尽可能使用单个字符名称(因此上面的列实际上是i、r、s、dr、ds、de、@987654333 @, ...),但它是如此非人类不可读,使用起来可能会让人困惑。

【问题讨论】:

  • “Cassandra 中的每一列都会产生 15 个字节的开销。” - 这是行的意思,对吗?所以在每一行中,每列有 15 个字节的开销,对吧?
  • @Kozuch 是的,那部分很清楚。
  • 我想补充一点,从 Cassandra 3.x 版本开始,我们不再因使用长列名而受到惩罚!但是,对于 Scylla 或其他仍在使用旧版本 SSTable 文件格式的人来说,这可能仍然是个问题。

标签: cassandra cql cql3 cassandra-2.0


【解决方案1】:

找出在这种情况下发生了什么的最简单方法是检查数据的 sstable2json (cassandra/bin) 表示。这将向您展示最终实际保存在磁盘上的内容。

这里是你的情况的例子

 [
 {"key": "4b6579","columns": [
       ["rid1:ssid1:","",1401469033325000],
       ["rid1:ssid1:end_date","2004-10-03 00:00:00-0700",1401469033325000],
       ["rid1:ssid1:report_date","2004-10-03 00:00:00-0700",1401469033325000],
       ["rid1:ssid1:start_date","2004-10-03 00:00:00-0700",1401469033325000], 
       ["rid1:ssid1:subset_descr","descr",1401469033325000],
       ["rid1:ssid1:x","1",1401469033325000], 
       ["rid1:ssid1:y","5.5",1401469033325000],
       ["rid1:ssid1:z","1",1401469033325000],
       ["rid2:ssid2:","",1401469938599000],
       ["rid2:ssid2:end_date", "2004-10-03 00:00:00-0700",1401469938599000],
       ["rid2:ssid2:report_date","2004-10-03 00:00:00-0700",1401469938599000],
       ["rid2:ssid2:start_date","2004-10-03 00:00:00-0700",1401469938599000], 
       ["rid2:ssid2:subset_descr","descr",1401469938599000],
       ["rid2:ssid2:x","1",1401469938599000],
       ["rid2:ssid2:y","5.5",1401469938599000],
       ["rid2:ssid2:z","1",1401469938599000]
 }
 ]

正如您在上面看到的,每个分区(每个 sstable)都会保存一次分区键的值,在这种情况下,列名根本无关紧要,因为它是隐含的给定表。聚类列的列名也不存在,因为在 C* 中,如果不指定键的所有部分,则不允许插入。

虽然确实有列名,但如果对行进行部分更新,则需要这样做,以便可以在没有其余行信息的情况下保存它。您可以想象更新一行中的单个列字段,以指示这是 C* 当前使用列名的哪个字段,但有票可以将其更改为更小的表示形式。 https://issues.apache.org/jira/browse/CASSANDRA-4175

生成这个

cqlsh
CREATE TABLE mykeyspace.mytable(   id text,   report_id text,   subset_id text,   report_date timestamp,   start_date timestamp,   end_date timestamp,   subset_descr text,   x int,   y double,   z int,   PRIMARY KEY (id, report_id, subset_id) );
INSERT INTO mykeyspace.mytable (id, report_id , subset_id , report_date , start_date , end_date , subset_descr ,x, y, z) VALUES ( 'Key', 'rid1','ssid1', '2004-10-03','2004-10-03','2004-10-03','descr',1,5.5,1);
INSERT INTO mykeyspace.mytable (id, report_id , subset_id , report_date , start_date , end_date , subset_descr ,x, y, z) VALUES ( 'Key', 'rid2','ssid2', '2004-10-03','2004-10-03','2004-10-03','descr',1,5.5,1);
exit;
nodetool flush
bin/sstable2json $DATA_DIR/mytable/mykeyspace-mytable-jb-1-Data.db 

【讨论】:

  • 磁盘压缩显然会消除由于列名重复而导致的大部分损失,因此对磁盘空间来说并不是什么大问题,但对于读取它就有点问题了。
  • 阅读是什么意思?我怎样才能知道压缩数据在磁盘上的实际大小?
  • 当实际从磁盘读取数据时,它将被解压缩,因此您在服务器上读取的内存中将具有完整的列名。浪费内存。要找出磁盘上的大小,您可以检查 nodetool 状态,或者只计算数据目录中所有文件的大小 - 快照
猜你喜欢
  • 2012-12-29
  • 2017-06-20
  • 1970-01-01
  • 2012-06-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-29
  • 2013-09-08
相关资源
最近更新 更多