【问题标题】:Cassandra time sliced data model for unknown data未知数据的 Cassandra 时间切片数据模型
【发布时间】:2013-11-18 01:54:00
【问题描述】:

我通过说明这个问题来警告这个问题:我对 NoSQL 有点陌生,对 Cassandra 也很陌生,但它似乎很适合我正在尝试做的事情。

假设我有一个传感器列表,以合理的时间间隔提供输入。我提出的数据模型是按传感器的名称、它所在的位置(区域)和日期(写为 yyyyMMdd)进行分区,然后按读数发生的实际时间对当天的读数进行聚类。想法是“在日期 B 从传感器 A 获取所有读数”的查询应该非常快。到目前为止,我认为很好。表/CF 在 CQL 中如下所示:

CREATE TABLE data (
    area_id int,
    sensor varchar,
    date ascii,
    event_time timeuuid,
    PRIMARY KEY ((area_id, sensor, date), event_time)
) WITH CLUSTERING ORDER BY (event_time DESC);

但这实际上并不包含任何数据,而且我不确定如何将其添加到模型中。每个读数(来自同一个传感器)都可以有一组不同的任意数据,我不会提前知道这是什么。例如。我可以得到温度数据,我可以得到湿度,我可以得到两者,或者我可以得到我以前从未见过的东西。由实际记录数据的人决定他们想要提交什么(不是从自动传感器读取)。

鉴于我想对这些数据(基本上是 UGC)进行查询操作,我有哪些选择?查询通常包括对数据的计数(例如,计数传感器 A 在日期 B 的读数,其中 some_ugc_valueX = C 和 some_ugc_valueY = D)。值得注意的是,将有比通常一次查询更多的数据点。一个读数可能有 20 个数据值,但可能只有 2 或 3 个会被查询 - 只是提前不知道哪个。

目前我想到的是:

  1. 将读取的每个传感器的数据存储为地图类型。这肯定会使模型变得简单,但我的理解是查询会很困难?我想我需要为每个传感器读数拉回整个地图,然后检查值并在 Storm/Hadoop/whatever 中的 Cassandra 之外对其进行计数。
  2. 将每个用户值存储为另一列(带有 event_time uuid 的复合列)。这意味着不使用 CQL,因为它不支持在插入时添加任意新列。然而,Thrift API 确实允许这样做。这意味着我可以让 Cassandra 自己进行计数。

也许我做错了?也许 Cassandra 甚至不是这类数据的最佳选择?

【问题讨论】:

    标签: cassandra


    【解决方案1】:

    tl;博士。你不能同时选择速度和绝对的灵活性;-)

    基于来自用户生成内容的数据的查询将变得很复杂 - 您将无法生成一个万能的表定义来允许对基于 UGC 内容的查询进行快速响应。即使您选择使用 Maps,Cassandra 也必须在每个查询中反序列化整个数据结构,因此对于大型 Maps 来说,这并不是一个真正的选择——正如您在问题中所建议的那样,情况很可能就是这种情况。

    另一种方法可能是以序列化形式存储传感器数据,例如 json。这将为存储的内容提供最大的灵活性——但代价是无法进行复杂的查询。序列化/反序列化负担被推送到客户端,所有数据都通过网络发送。这是一个简单的例子:

    表创建(比您的示例稍微简单 - 我已删除 date):

    create table data(
      area_id int, 
      sensor varchar, 
      event_time timeuuid, 
      data varchar, 
      primary key(area_id,sensor,event_time)
    );
    

    插入:

    insert into data(area_id,sensor,event_time,data) VALUES (1,'sensor1',now(),'["datapoint1":"value1"]');
    insert into data(area_id,sensor,event_time,data) VALUES (1,'sensor2',now(),'["datapoint1":"value1","count":"7"]');
    

    按 area_id 和传感器查询:

    >select area_id,sensor,dateof(event_time),data from data where area_id=1 and sensor='sensor1';
    
     area_id | sensor  | dateof(event_time)       | data
    ---------+---------+--------------------------+-------------------------
           1 | sensor1 | 2013-11-06 17:37:02+0000 | ["datapoint1":"value1"]
    
    (1 rows)
    

    按area_id查询:

    > select area_id,sensor,dateof(event_time),data from data where area_id=1;
    
     area_id | sensor  | dateof(event_time)       | data
    ---------+---------+--------------------------+-------------------------------------
           1 | sensor1 | 2013-11-06 17:37:02+0000 |             ["datapoint1":"value1"]
           1 | sensor2 | 2013-11-06 17:40:49+0000 | ["datapoint1":"value1","count":"7"]
    
    (2 rows)
    

    (使用[cqlsh 4.0.1 | Cassandra 2.0.1 | CQL spec 3.1.1 | Thrift protocol 19.37.0]测试。)

    【讨论】:

    • 我猜这类似于使用地图字段,但允许客户端处理(反)序列化,它可能会做得更好。可能值得注意的是,我希望 JSON 数据点比任何查询中通常使用的更多(我将更新问题以反映这一点)。这意味着“通过网络”拉动完整的有效载荷以反序列化可能只需要 1 或 2 个字段的情况(尽管不能保证)。如果一天有 100 万条记录,那么这就是很多数据。不过,这可能是其他人正在做的事情——我不确定。
    • 我认为计数应该保存在一个单独的表中 - 如果您正在寻找最佳速度,您应该考虑每个查询类型一个表。
    • 我已经详细说明了我的答案 - 您关于完整有效负载的观点是一个重要的考虑因素。您将灵活地权衡有效载荷大小。如果您对传感器数据有更多了解,您可以为每种传感器类型编写自定义表,并对它们进行更复杂的查询。不幸的是,如果不知道先验结构,您将无法做到这一点。
    • 我想我将在这里使用混合解决方案。我将在类似于您描述的模式中序列化整个事件。对于任何复杂的操作,将其拉出并反序列化将是唯一的方法。我还将按值对再次存储数据(嘿,存储很便宜),如下所示: CREATE TABLE sensor_composite ( area_id int, sensor varchar, date ascii, property varchar, event_time timeuuid, value varchar PRIMARY KEY ((area_id, sensor, date), property, event_time) ) ... 目的是可以用于快速属性读取。
    • 是的,我认为这是有道理的——非规范化是新的规范化;-)
    猜你喜欢
    • 2013-08-02
    • 1970-01-01
    • 2013-04-17
    • 2013-07-13
    • 2015-03-22
    • 2017-04-12
    • 2015-12-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多