作为一个支持完整 SQL 的关系型数据库,TimescaleDB 支持灵活的数据类型,可根据不同用户的实际情况进行优化。因此,Timescale 与大多数其它使用“窄表”模型时序数据库有所不同。 具来说来,TimescaleDB 可以支持宽表模型和窄表模型。我们使用一个物联网(IoT)的示例来讨论这两个模型在性能平衡和意义上的不同之处。 想象有超过1000个物联网设备被分布在各地,以各种不同的时间间隔来收集环境数据。这些数据包含:
举例来说,你接收到的数据可能是下面这样的:
现在,我们来看看模拟这些数据的各种方法。 窄表模型大多数时序数据库表示数据无外乎以下几种方式:
在这种模型中,每个指标/标签集的组合可以看做一个独立的包含一个时间/值的键值对的时间序列。 使用我们的上述的例子,这个方法将会产生九个不同的时间序列,每个时间序列都是由一个唯一的标签集合来定义的。 1. {name: cpu_1m_avg, device_id: abc123, location_id: 335, dev_type: field}
2. {name: cpu_1m_avg, device_id: def456, location_id: 335, dev_type: roof}
3. {name: cpu_1m_avg, device_id: ghi789, location_id: 77, dev_type: roof}
4. {name: free_mem, device_id: abc123, location_id: 335, dev_type: field}
5. {name: free_mem, device_id: def456, location_id: 335, dev_type: roof}
6. {name: free_mem, device_id: ghi789, location_id: 77, dev_type: roof}
7. {name: temperature, device_id: abc123, location_id: 335, dev_type: field}
8. {name: temperature, device_id: def456, location_id: 335, dev_type: roof}
9. {name: temperature, device_id: ghi789, location_id: 77, dev_type: roof} 这些时间序列的范围大小是与每个标签的基数的交叉乘积,也就是(# names) × (# device ids) × (# location ids) × (device types)。随着基数的增加,一些时间序列数据库会遇到性能问题,最终限制了可以存储在单个数据库中的设备类型和设备的数量。 TimescaleDB 支持窄表模型,并且不会像其他时间序列数据库那样受到相同的基数限制。如果您单独收集每个指标,那么窄表模型就有意义。它允许您通过添加新标记来添加新指标,而无需进行正式的架构更改。 但是,如果要收集具有相同时间戳的很多度量标准,则窄表模型就不具有高效性,因为它需要为每个度量标准写入时间戳。这最终会导致更高的存储和获取要求。此外,关联不同度量标准的查询也更复杂,因为要关联的每一个额外度量标准都需要一个 JOIN。如果您想同时查询多个指标,用宽表格式存储会更加便捷,这个我们将在下一节中进行介绍。 宽表模型TimescaleDB 能够轻松支持宽表模型。由于该模型不需要 JOIN,所以跨越多个度量标准的查询更为容易。此外,由于该模型只为多个度量标准编写了一个时间戳,因此提取速度也更快。 一个典型的宽表模型将匹配一个在给定时间戳中收集的多个度量指标的典型数据流:
在这里,每一行都是一个新的读数,在给定的时间内有一组测量和元数据。这使我们能够保留数据中的关系,并提出比以前更多有趣或探索性的问题。 当然,这不是一种新格式:它是人们在关系数据库中所经常见到的内容。 关系型数据中的 JOINsTimescaleDB 的数据模型与关系型数据库有着很多相似之处:它支持 JOINs。具体的来说,它可以在副表中存储其他元数据,然后在查询时使用该数据。 在我们的示例中,有一个单独的位置表,能够将 location_id 映射到该位置的其他元数据中。例如:
然后在查询时,通过关联这两个表,可以提出如下问题: 在 zip_code 为 10017 时,我们设备的 free_mem 平均值是多少? 如果没有 JOINs,我们需要对其数据进行反规范化,并将每个测量行存储在所有元数据中。这会造成数据膨胀,并使数据管理变得更加困难。 有了 JOINs,我们可以独立存储元数据,并且更快捷地更新映射。 例如,如果我们想更新 location_id 为 77 的"region"的数据(例如将"Massachusetts"修改为"Boston"),我们可以直接进行更改,而无需返回并覆盖历史数据。 |