Cassandra时间序列数据的分区键

10
我将作为时间序列数据库测试Cassandra。
我创建了以下数据模型:
CREATE KEYSPACE sm WITH replication = {
  'class': 'SimpleStrategy',
  'replication_factor': 1
};

USE sm;

CREATE TABLE newdata (timestamp timestamp,
  deviceid int, tagid int,
  decvalue decimal,
  alphavalue text,
  PRIMARY KEY (deviceid,tagid,timestamp));

在主键中,我将设备ID设置为分区键,这意味着具有相同设备ID的所有数据将写入一个节点(这是指一台机器还是一个分区?每个分区最多可以有20亿行),而且如果我在同一节点内查询数据,则检索速度会很快,我理解正确吗?我对Cassandra比较陌生,对分区键和聚集键有点困惑。
我的大多数查询将如下所示:
- 选择已知设备ID和标签ID的最新时间戳 - 选择已知设备ID和标签ID以及时间戳的decvalue - 选择已知设备ID和标签ID以及时间戳的alphavalue - 在时间范围内选择已知设备ID和标签ID的所有内容 - 在时间范围内选择已知设备ID的所有内容
我将拥有大约2000个设备ID,每个设备ID将拥有60个tagid/value对。我不确定它是否会成为一个宽行,包含设备ID、时间戳、tagid/value、tagid/value...
1个回答

27

我对Cassandra还很陌生,对分区键和聚簇键有点困惑。

听起来你已经了解了分区键,那我会补充一下:你的分区键帮助Cassandra确定在集群中存储你的数据的令牌范围(哪个令牌范围)。每个节点负责几个主要令牌范围(假设使用vnodes)。当你的数据被写入到数据分区时,它会按照聚簇键进行排序。这也是它存储在磁盘上的方式,因此请记住你的聚簇键决定了你的数据存储顺序。

每个分区最多可以有20亿行

这不完全正确。每个分区最多支持20亿个单元格。一个单元格本质上是一个列名/值对。而你的聚簇键本身就相当于一个单元格。所以,通过计算每个CQL行中存储的列值数量来计算单元格的数量,并且如果你使用聚簇列,则再加上一个。

根据你的宽行结构,你可能会有比2亿行更少的限制。此外,这只是存储限制。即使你在单个分区中成功存储了100万个CQL行,查询该分区也会返回大量数据,导致查询效率低下,可能会超时。

如果我查询同一节点内的数据,检索速度会很快,对吗?

至少它肯定比多键查询命中多个节点要快。但它是否“快”取决于其他因素,例如你的行有多宽,以及你何时执行删除和原地更新等操作。

我的大部分查询将是以下内容:

select lastest timestamp of know deviceid and tagid
Select decvalue of known deviceid and tagid and timestamp
Select alphavalue of known deviceid and tagid and timestamp
select * of know deviceid and tagid with time range
select * of known deviceid with time range
您当前的数据模型可以支持除最后一个查询之外的所有查询。为了在timestamp上执行范围查询,您需要将数据复制到一个新表中,并构建一个支持该查询模式的PRIMARY KEY。这被称为“基于查询的建模”。我会像这样构建一个查询表:
CREATE TABLE newdata_by_deviceid_and_time (
  timestamp timestamp,
  deviceid int,
  tagid int,
  decvalue decimal,
  alphavalue text,
  PRIMARY KEY (deviceid,timestamp));

那张表可以支持以timestamp为条件的范围查询,并且在deviceid上分区。

但我看到这两种模型中最大的问题是"行增长不受限制"。基本上,随着您收集越来越多的设备值,您将接近每个分区的20亿个单元格限制(而且很可能在此之前就会变得非常缓慢)。你需要做的是使用一种称为“时间分桶”的建模技术。

以本例说明,我确定按月份分桶会使我保持在2亿单元格限制以下,并且允许我所需的日期范围灵活性。如果是这样,我将添加一个额外的分区键 monthbucket,我的(新)表将如下所示:

CREATE TABLE newdata_by_deviceid_and_time (
  timestamp timestamp,
  deviceid int,
  tagid int,
  decvalue decimal,
  alphavalue text,
  monthbucket text,
  PRIMARY KEY ((deviceid,monthbucket),timestamp));

现在,当我想要查询特定设备和日期范围内的数据时,我还会指定monthbucket

SELECT * FROM newdata_by_deviceid_and_time
WHERE deviceid='AA23' AND monthbucket='201603'
AND timestamp >= '2016-03-01 00:00:00-0500'
AND timestamp < '2016-03-16 00:00:00-0500';

请记住,monthbucket仅为示例。 对于您来说,使用季度甚至年可能更有意义(假设您在一年内不会存储太多值 deviceid)。


非常感谢你,Aaron!那真的很有帮助...我会按照你建议的去做,同时也尝试精简我的数据模型,因为一些原因Cassandra消耗CPU、RAM、IO和存储比Mongo高得多。 - Phuong Le
嗨,亚伦,为了优化这个数据模型,我能否通过使用映射 {'tagid1':value1,'tagid2':value2} 来创建表格?这样做是否可以减少硬件需求而不影响性能? - Phuong Le
@PhuongLe 不,将所有数据存储在映射或连接的字符串中不会获得任何性能提升。 - Aaron
1
感谢 @Aaron 的回应!我在这里提出了一个问题,并进行了编辑以提供更多背景信息。期待您的想法。再次感谢!https://dev59.com/4X4QtIcB2Jgan1znmy-f - realnsleo
1
谢谢 @Aaron。我想在我的情况下只使用 monthbucket 和 timestamp 作为聚簇键,以便能够在 timestamp 列中查询时间范围 PRIMARY KEY (monthbucket, timestamp) - undefined
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接