Redis实现时间序列数据
时间序列数据
例如需要周期性地统计近万台设备的实时状态,包括设备 ID、压力、温度、湿度,以及对应的时间戳。
这些与发生时间相关的一组数据,就是时间序列数据。这些数据的特点是没有严格的关系模型,记录的信息可以表示成键和值的关系(例如,一个设备 ID 对应一条记录),所以,并不需要专门用关系型数据库(例如 MySQL)来保存。
基于 Hash 和 Sorted Set 保存时间序列数据
我们可以把时间戳作为 Hash 集合的 key,把记录的设备状态值作为 Hash 集合的 value。但是,Hash 类型有个短板:它并不支持对数据进行范围查询。
为了能同时支持按时间戳范围的查询,可以用 Sorted Set 来保存时间序列数据,因为它能够根据元素的权重分数来排序。我们可以把时间戳作为 Sorted Set 集合的元素分数,把时间点上记录的数据作为元素本身。
但是我们又会面临一个新的问题,也就是我们要解答的第二个问题:如何保证写入 Hash 和 Sorted Set 是一个原子性的操作呢?
这里可以使用redis事务,当多个命令及其参数本身无误时,MULTI 和 EXEC 命令可以保证执行这些命令时的原子性。
但是因为 Sorted Set 只支持范围查询,无法直接进行聚合计算,所以,我们只能先把时间范围内的数据取回到客户端,然后在客户端自行完成聚合计算。这个方法虽然能完成聚合计算,但是会带来一定的潜在风险,也就是大量数据在 Redis 实例和客户端间频繁传输,这会和其他操作命令竞争网络资源,导致其他操作变慢。
所以如果需要进行大量的聚合计算,同时网络带宽条件不是太好时,Hash 和 Sorted Set 的组合就不太适合了。此时,使用 RedisTimeSeries 就更加合适一些。
基于 RedisTimeSeries 模块保存时间序列数据
RedisTimeSeries 是 Redis 的一个扩展模块。它专门面向时间序列数据提供了数据类型和访问接口,并且支持在 Redis 实例上直接对数据进行按时间范围的聚合计算。
因为 RedisTimeSeries 不属于 Redis 的内建功能模块,在使用时,我们需要先把它的源码单独编译成动态链接库 redistimeseries.so,再使用 loadmodule 命令进行加载,如下所示:
当用于时间序列数据存取时,RedisTimeSeries 的操作主要有 5 个:
用 TS.CREATE 命令创建时间序列数据集合;
TS.CREATE device:temperature RETENTION 600000 LABELS device_id 1用 TS.ADD 命令插入数据;
TS.ADD device:temperature 1596416700 25.1用 TS.GET 命令读取最新数据;
TS.GET device:temperature用 TS.MGET 命令按标签过滤查询数据集合;
TS.MGET FILTER device_id!=2用 TS.RANGE 支持聚合计算的范围查询。
TS.RANGE device:temperature 1596416700 1596417120 AGGREGATION avg 180000








