Redis技术解锁Redis时间序列数据的应⽤
⼀、时序数据介绍
什么是时间序列数据(Time Series Data,TSD,以下简称时序)从定义上来说,就是⼀串按时间维度索引的数据。简单的说,就是这类数据描述了某个被测量的主体在⼀个时间范围内的每个时间点上的测量值。它普遍存在于IT基础设施、运维监控系统和物联⽹中。
对时序数据进⾏建模的话,会包含三个重要部分,分别是:主体,时间点和测量值。时序数据从时间维度上将孤⽴的观测值连成⼀条线,从⽽揭⽰软硬件系统的状态变化。孤⽴的观测值不能叫时序数据,但如果把⼤量的观测值⽤时间线串起来,我们就可以研究和分析观测值的趋势及规律。
⼆、时序数据特征
数据写⼊:数据持续⾼速⽣成,持续⾼并发写⼊,数据点⼀旦插⼊数据库,就不会发⽣更改,可设置过期时间。
⾮结构化标签:时序数据通常是由许多来源在很长⼀段时间内连续产⽣的。例如,在IoT⽤例中,每个传感器都是时序数据的来源。在这种情况下,序列中的每个数据点都将源信息和其他传感器测量结果存储为标签。来⾃每个来源的数据标签可能不符合相同的结构或顺序。
数据价值递减:将来只有适当时间范围内的汇总数据摘要才有意义,存在明显的冷热数据,⼀般只会频繁查询近期数据。
三、Redis 时序数据⽅案
Redis是现在最受欢迎的NoSQL数据库之⼀,redis 内置了多种常⽤数据结构,适⽤于多种应⽤场景:缓存、队列、分布式锁、排⾏榜等等。除此之外redis 还可以⾃定义扩展模块引⼊更多数据结构, ⽐如引⼊RediSearch 模块⽤于redis ⽀持全⽂检索( 了解更多有⽤的模块可访问 )。以下主要讲述redis在时序数据存储的应⽤⽅案:
3.1 使⽤Sorted Set存储
SortedSets 是redis 内置的数据结构,跟set相⽐⽀持按权重值存储数据,⽀持权重的范围查询。时序数据的场景下,可将数据时间戳作为权重值存储,key存储具体的度量维度。
缺点:
1、SortedSets 不是⼀种节约内存的数据结构
2、写⼊性能不⾼
3、内置缺少聚合⼯具,只⽀持客户端程序聚合,造成代码繁琐且⽹络IO占⽤⾼
3.2 使⽤Stream 存储
Redis Stream 是 Redis 5.0 版本新增加的数据结构,使⽤ Rax(Radix树的单独实现)实现,与 Sorted Sets 相⽐,Redis Streams 增强了插⼊和读取的性能。但 Stream 主要⽤于消息队列,仍然缺少了特定于时间序列的聚合⼯具。
缺点:
内置缺少聚合⼯具。
3.3 使⽤RedisTimeSeries存储
RedisTimeSeries 是专门为Redis 存取时序数据⽽设计的扩展模块。由于 RedisTimeSeries 不属于 Redis 的内置数据结构,在使⽤时,需要先把它的源码单独编译成动态链接库 redistimeseries.so。
3.3.1模块安装
步骤1:⽣成动态库
linux执⾏命令编译:
cd RedisTimeSeries
make setup
如果编译不成功,有个捷径获取动态库:
拉取docker 镜像redislabs/redistimeseries,启动容器可在容器中到 redistimeseries.so⽂件
步骤2:加载动态⽂件到redis
在 f ⽂件中加⼊
loadmodule /path/to/redistimeseries.so
在启动的 client 中输⼊
module load /path/to/redistimeseries.so
在服务器启动是加载
redis-server --loadmodule /path/to/redistimeseries.so
步骤3:查看timeseries模块是否安装成功
root@1738e25ad4eb:/data# redis-cli
127.0.0.1:6379> module list
1) 1) "name"
2) "timeseries"
3) "ver"
4) (integer) 10608
遇到的问题与解决⽅案:
(1)解决make的版本低不能使⽤,升级make
tar xf make-4.
cd make-4.3/
# 安装到指定⽬录
./configure  --prefix=/usr/local/make
make && make install
make -v
# 此时的 make 还是3.82 与环境变量有关系,可执⾏下⾯操作
mv /usr/bin/make /usr/lib/make.old
ln -s /usr/local/make/bin/make /usr/bin/make
(2)解决报错 libssl.so.1.1: cannot open shared object file: No such file or directory
参考:
(3)解决libc.so.6版本问题 /lib64/libc.so.6:version 'GLIBC_XXX' not found
参考:
3.3.2 主要命令
以下命令基本可以实现时序数据存储与查询:
1、TS.CREATE:创建⼀个时序数据集合
命令格式:
TS.CREATE key [RETENTION retentionTime] [ENCODING [UNCOMPRESSED|COMPRESSED]] [CHUNK_SIZE size] [DUPLICATE_POLICY policy] [LABELS label value..]
参数说明:
- RETENTION: 与最后的事件时间相⽐样本的最⼤年龄(以毫秒为单位),默认值为0,代表数据不会过期
- ENCODING: 编码模式COMPRESSED使⽤压缩算法存储 ,UNCOMPRESSED将原始样本保存在内存中
-
CHUNK_SIZE: 数据分配的内存⼤⼩,单位字节,必须是 8 的倍数,默认值:4096。
- DUPLICATE_POLICY: 重复样本策略配置。
- LABELS: 标签是key的元数据,以键值对⽅式存储
⽰例命令:
TS.CREATE temperature:2:32 RETENTION 60000 DUPLICATE_POLICY MAX LABELS sensor_id 2 area_id 32
2、TS.ADD:给某个key 新增时序数据,如果不存在则会创建集合
命令格式:
TS.CREATE key [RETENTION retentionTime] [ENCODING [UNCOMPRESSED|COMPRESSED]] [CHUNK_SIZE size] [DUPLICATE_POLICY policy] [LABELS label value..]
复杂度:
如果在时间序列上存在压缩规则,则 TS.ADD性能可能会降低。TS.ADD当 M 是压缩规则的数量或没
有压缩的 O(1) 时 ,复杂度 总是O(M)。
参数说明:
- timestamps: 时间戳,单位毫秒
- value:  double 类型的数值
其他参数是可选的,与TS.CREATE ⼀致
⽰例命令:
TS.CREATE temperature:2:32 RETENTION 60000 DUPLICATE_POLICY MAX LABELS sensor_id 2 area_id 32
3、TS.MADD:给某个key 批量添加数据
命令格式:
TS.MADD key timestamp value [key timestamp value ...]
⽰例命令:
127.0.0.1:6379>TS.MADD temperature:2:32 1548149180000 26 cpu:2:32 1548149183000 54
1) (integer) 1548149180000
2) (integer) 1548149183000
127.0.0.1:6379>TS.MADD temperature:2:32 1548149181000 45 cpu:2:32 1548149180000 30
1) (integer) 1548149181000
2) (integer) 1548149180000
4、TS.DEL:删除某个key 某个时间范围的样本数据
命令格式:
TS.DEL key fromTimestamp toTimestamp
返回值:被移除的样本数
⽰例命令:
127.0.0.1:6379>TS.DEL temperature:2:32 1548149180000 1548149183000
(integer) 150
5、TS.CREATERULE:创建数据压缩规则
命令格式:
TS.CREATERULE sourceKey destKey AGGREGATION aggregationType timeBucket
参数说明:
- sourceKey : 源时间序列的键名
- destKey : ⽬标时间序列的键名
- aggregationType : 聚合类型avg、sum、min、max、range、count、first、last、std.p、std.s、var.p、var.s - timeBucket : 以毫秒为单位的聚合时间桶
6、TS.DELETERULE: 删除压缩规则
命令格式:
TS.DELETERULE sourceKey destKey
参数说明:
- sourceKey : 源时间序列的键名
- destKey : ⽬标时间序列的键名
7、TS.RANGE:范围查询,可配合聚合函数使⽤, 需要指定key
命令格式:
TS.RANGE key fromTimestamp toTimestamp
[FILTER_BY_TS TS1 TS2 ..]
[FILTER_BY_VALUE min max]
[COUNT count] [ALIGN value]
[AGGREGATION aggregationType timeBucket]
参数说明:
- FILTER_BY_TS: 返回特定时间戳的数据
- FILTER_BY_VALUE: 过滤结果中最⼤值或最⼩值
- COUNT:返回的结果的最⼤数量
- AGGREGATION:搭配聚合函数⽣成聚合桶bucket
- timeBucket: 聚合桶的时间范围(单位毫秒)
⽰例命令:
127.0.0.1:6379> TS.RANGE temperature:3:32 1548149180000 1548149210000 AGGREGATION avg 5000
1) 1) (integer) 1548149180000
2) "26.199999999999999"
2) 1) (integer) 1548149185000
2) "27.399999999999999"
3) 1) (integer) 1548149190000
2) "24.800000000000001"
8、TS.GET:获取指定key的最新⼀条数据
命令格式:
TS.GET key
复杂度:
TS.GET 复杂度为 O(1)
⽰例命令:
127.0.0.1:6379> TS.GET temperature:2:32
1) (integer) 1548149279
2) "23"
9、TS.MGET:获取与特定过滤器匹配的所有key的最新⼀条数据。
命令格式:
TS.MGET [WITHLABELS | SELECTED_LABELS label1 ..]
参数说明:
- FILTER: 过滤器
- WITHLABELS: 包含表⽰时间序列元数据标签的标签-值对
复杂度:
TS.MGET 复杂度为 O(n)。
⽰例命令:
#过滤area_id=32的所有key 的最新数据
redis八种数据结构
127.0.0.1:6379> TS.MGET FILTER area_id=32
1) 1) "temperature:2:32"
2) (empty list or set)
3) 1) (integer) 1548149181000
2) "30"
2) 1) "temperature:3:32"
2) (empty list or set)
3) 1) (integer) 1548149181000
2) "29"
3.3.3客户端库
Project Language License Author
Java BSD-3