Redis核心技术与实战【学习笔记】 - 8.Redis 时间序列数据处理

在做 web 产品是,都会有这么一个需求: 记录用户在网站或 APP 上的点击行为数据,来分析用户行为。这里的数据一般包括用户 ID、行为类型(如浏览、登录、下单等)、行为发生的时间戳。

userID, type, timeStamp

与之类似,物联网项目需要周期性的统计设备的实时状态,包括包括设备 ID、压力、温度、湿度,以及对应的时间错:

deviceId, pressure, temperature, humidity, timestamp

这些与发生时间相关的一组数据,就是时间序列数据。这些数据的特点是没有严格的关系模型,记录的信息可以表示成键和值的关系(如,一个设备 ID 对应一条记录),所以,并不需要专门用关系型数据库来保存。而 Redis 的键值数据模型,正好可以满足这里的数据存取需求。

时间序列数据的读写特点

时间序列数据通常是持续高并发写入的,例如,需要连续记录数万个设备的实时状态值。同时,时间序列数据被记录后通常就不变了,因为它就是代表一个设备在某个时刻的状态值。

所以,这种数据的写入特点很简单,就是插入数据快,这就要求我们选择的数据类型,在进行数据插入时,复杂度要低,尽量不要阻塞。你可能第一时间会想到用 Redis 的 String、Hash 类型来保存,因为它们插入复杂度都是 O(1),但是,我们之前说过,String 类型在记录小数据时,元素局的内存开销比较大,不太适合保存大量数据。

在查询时间序列数据时,既有对单条记录的查询,也有对某个时间范围内的数据查询。除此之外,还有一些更复杂的查询,比如对某个时间范围内的数据做聚合计算。这里的聚合计算,就是对符合查询条件的所有数据做计算,包括计算均值、最大/最小值、求和等。例如,统计某个时间段内的设备温度最大值,来判断设备是否有故障发生。

用一次词概括时间序列数据的“读”,就是查询模式多

我们来分析下:

  • 针对时间序列数据的“写要快”,Redis 的高性能写特性直接就可以满足了;
  • 而针对“查询模式多”的特点(单点查询、范围查询、聚合计算),Redis 提供了两种方案,分别是基于 Hash 和 Sorted Set 实现,以及基于 RedisTimeSeries 模块实现。

基于 Hash 和 Sorted Set 保存时间序列数据

Hash 和 Sorted Set 组合方式有一个明显的好处:它们是 Redis 内在的数据类型,代码成熟度和性能文档。所以基于这两个数据类型保存时间序列,系统稳定性是可以预期的。

第一个问题,为什么保存时间序列数据,要同时使用这两类数据呢?

因为 Hash 类型有一个特点,它可以实现对单键的快速查询。这就满足了时间序列数据的单建值查询需求。我们可以把时间戳作为 Hash 集合的 key,把设备的状态作为 Hash 集合的 value。

可以看下用 Hash 集合记录设备的温度值的示意图:
在这里插入图片描述

当我们想要查询某个时间点或者是多个时间点上的温度数据时,直接使用 HGET 命令或者 HMGET 命令,就可以分别获得 Hash 集合中的一个 key 或 多个 key 的 value 值了。

例如,用 HGET 命令查询 202401010905 这个时刻的温度值,使用 HMGET 查询 202401010905、202401010907、202401010908 这三个时刻的温度值:

HGET device:temperature 202401010905 
"25.1"

HMGET device:temperature 202401010905 202401010907 202401010908 
"25.9"
"25.1"
"25.3"

用哈希类型来实现单键的查询很简答。但是,Hash 类型不支持对数据进行范围查询

因为 Hash 类型的底层结构是哈希表,并没有对数据进行有序索引。所以,要对 Hash 集合进行范围查询的话,就要扫描 Hash 集合中所有的数据,在把这些数据取到客户端进行排序。很显然,这样的效率很低。

为了能同时支持按时间戳范围的查询,可以用 Sorted Set 来保存时间序列数据,因为它可以根据元素的权重分数来排序。把时间戳作为 Sorted Set 集合的元素分数,把时间点上记录的数据作为元素本身。

还是以设备温度的时间序列数据为例,进行解释。下图显示了用 Sorted Set 集合保存的结果
在这里插入图片描述
使用 ZRANGEBYSCORE 命令,按照输入的最大时间戳和最小时间戳来查询这个时间范围内的温度值了。如下所示:

ZRANGEBYSCORE device:temperature 202401010907 202401010910
"25.9"
"24.9"
"25.2"
"25.1"

注意:sorted set 使用时间戳作为 score,温度作为 member 的问题?
温度大概率会出现相同的,这个时候 ZADD 后会覆盖掉原有的数据。

现在,我们知道同时使用 Hash 和 Sorted Set,可以满足单个时间点和一个时间范围内的数据查询需求了。
第二个问题,如何保证写入 Hash 和 Sorted Set 是一个原子操作

原子操作,就是指执行多个写命令时,要么全部完成,要么都不完成。

这里涉及到了 Redis 的支持简单事务的 MULTI 和 EXEC 命令。当多个命令及其参数本身无误时, MULTI 和 EXEC 命令可以保证执行这些命令的原子性。

  • MULTI :表示一些列原子性操作的开始。收到这个命令后,Redis 就知道,接下来收到的命令需要放到一个内部队列中,后续一起执行,保证了原子性。
  • EXEC :表示一系列原子性操作的结束。一旦 Redis 收到这个命令,就表示所有保证原子性的操作都已经发送完成了。此时,Redis 开始执行刚才放到内部队列中的所有操作。

在这里插入图片描述
已保存设备状态信息的需求为例,执行下面的代码,把设备在 2024 年 1 月 1 日 9 时 5 分的温蒂,分别用 HSET 命令和 ZADD 命令写入 Hash 集合和 Sorted Set 集合。

MULTI
"ok"

HSET device:temperature 202401010905 26.8
"QUEUED"

ZADD device:temperature 202401010905 26.8
"QUEUED"

EXEC
"1"
"1"

可以看到,首先 Redis 收到了客户端执行的 MULTI 命令。然后,客户端再执行 HSET 和 ZADD 命令后,Redis 返回的结果为 “QUEUED”,这表示这两个命令暂时入队,先不执行;执行了 EXEC 后,HSET 和 ZADD 命令才真正执行,并返回结果。

第三个问题:如何对时间序列进行聚合计算?

聚合计算一般被用来周期性统计时间窗口内的数据汇总状态,在实时监控与预警等场景下,会频繁执行。

Sorted Set 支持范围查询,但无法直接进行聚合统计,所以,我们只能先把时间范围内的数据取回到客户端,然后在客户端完成聚合计算。

这个方案会带来一定的潜在风险:大量数据在 Redis 实例和客户端间频繁传输,这会和其他命令竞争网络资源,导致其他操作变慢。
假设我们需要每 3 分钟计算一次所有设备各指标的最大值,每个设备每 15 秒 记录一个指标值,1分钟就会记录 4 个值,3 分钟就会有 12 个值。假设需要统计的指标有 33 个,所以单个设备每 3 分钟记录的指标数据有将近 400个(33 * 12 = 396)。若有 1 万台设备,这样一来每 3 分钟就有将近 400 万条(396 * 1 万 = 396 万),数据需要在客户端和 Redis 实例之间传输。

为避免客户端和 Redis 实例间频繁的大量数据传输,我们可以使用 RedisTimeSeries 来保存时间序列数据。

RedisTimeSeries 支持直接在 Redis 上进行聚合计算。还是以刚才每 3 分钟算一次最大值为例。在 Redis 实例上直接聚合计算,那么,对于单个设备的一个指标值来说,每 3 分钟记录的 12 条数据可以聚合计算成一个值,单个设备每 3 分钟也就只有 33 个聚合值需要传输,可以减少大量数据传输对 Redis 实例网络的性能影响。

所以,如果我们只需要进行单个时间点查询或对某个范围查询的怠话,适合使用 Hash 和 Sorted Set 的组合,它们都是 Redis 内在的数据结构,性能好,稳定性高。但是,如何要进行大量的聚合计算,同时网络带宽条件不是太好,使用 RedisTimeSeries 就更加适合一些。

基于 RedisTimeSeries 模块保存时间序列数据

RedisTimeSeries 是 Redis 的一个扩展模块。它专门面向时间序列数据提供了数据类型和访问接口,并且支持在 Redis 实例上直接对数据进行按时间范围的聚合计算。

因为 RedisTimeSeries 不属于 Redis 的内建功能模块,在使用时,需要先把它的源码单独编译程动态链接库 redistimeseries.so,再使用 loadmodule 命令进行加载,如下所示:

loadmodule redistimeseries.so

具体安装 redistimeseries.so 过程,可参考 Redis 安装 redistimeseries.so(时间序列数据类型)教程 。

当用于时间序列数据存取时, RedisTimeSeries 的操作主要有 5 个:

  • 用 TS.CREATE 命令创建时间序列数据集合;
  • 用 TS.ADD 命令插入数据
  • 用 TS.GET 命令读取最新数据
  • 用 TS.MGET 命令按标签过滤查询数据集合
  • 用 TS.RANGE 支持聚合计算的范围查询

1. 用 TS.CREATE 命令创建时间序列数据集合

在 TS.CREATE 命令中,我们需要设置时间序列集合数据的 key 和数据过期时间(以毫秒为单位)。此外,我们还可以为数据集合设置标签,来表示数据集合的属性。

例如,我们执行下面的命令,创建一个 key 为 device:temperature 、数据有效期为 600s 的时间序列数据集合。也就是,这个集合中的数据创建了 600s 后,就会被自动删除。最后,我们给这个集合设置了一个标签属性 {device_id:1} ,表明这个数据集合中记录的是属于设备 ID 号为 1 的数据。

127.0.0.1:6379> TS.CREATE device:temperature RETENTION 600000 LABELS device_id 1
OK

2.用 TS.ADD 命令插入数据,用 TS.GET 命令读取数据

用 TS.ADD 命令往时间序列集合中插入数据,包括时间戳和具体的数值,并使用 TS.GET 命令读取数据集合中的最新一条数据。

执行下列 TS.ADD 命令时,就往 device:temperature 集合和中插入一条数据,记录的是设备在 2024 年 1 月 1 日 9 时 5 分的设备温度;

127.0.0.1:6379> TS.ADD device:temperature 1704071100000 25.1
(integer) 1704071100000

再执行 TS.GET 命令时,就会把刚刚插入的最新数据读取出来。

127.0.0.1:6379> TS.GET device:temperature
1) (integer) 1704071100000
2) 25.1

3.用 TS.MGET 命令按标签过滤查询数据集合

在保存多个设备的时间序列数据时,我们通常把不同设备的数据保存到不同集合中。此时,我们就可以使用 TS.MGET 命令,按照标签查询部分集合中的最新数据。在使用 TS.CREATE 创建数据集合时,可以给集合设置标签属性值。当进行查询时,就可以在查询条件中对集合标签属性进行匹配,最后的查询结果里只返回匹配上的集合中的最新数据。

举个例子。假设我们一共用 4 个集合为 4 个设备保存时间序列数据,设备 ID 号是 1、2、3、4,当创建数据集合时,把 device_id 设置为每个集合的标签。此时,我们就可以使用下列 TS.MGET 命令,以及 FILTER 设置(这个配置项用来设置集合标签的过滤条件),查询 device_id 不等于 2 的所有其他设备的数据集合,并返回集合中最新的一条数据。

HMGET FILTER device_id!=2

4.用 TS.RANGE 支持聚合计算的范围查询

对时间序列数据进行聚合计算是,可以使用 TS.RANGE 命令,指定要查询的数据的时间范围,同时用 AGGERGATION 参数指定要执行的聚合计算类型。RedisTimeSeries 支持的聚合计算类型很丰富,包括:

  • 求平均值: avg
  • 求最大值:max
  • 求最小值:min
  • 求和:sum
  • 等等

例如,我们可以按照每 180s 的时间窗口,对时间段内的数据进行均值计算。

TS.RANGE device:temperature 1704071106700 1704071507120 AGGREGATION avg 180000

与使用 Hash 和 Sorted Set 来保存时间序列数据相比,RedisTimeSeries 是专门为时间序列数据访问设计的扩展模块,能支持在 Redis 实例上直接进行聚合计算,以及按标签属性过滤查询数据集合,当我们需要频繁进行聚合计算,以及从大量集合中筛选出特定设备或用户的数据集合时,RedisTimeSeries 就可以发挥优势了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/358788.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Prometheus的pod部署

创建命名空间和账户以及集群账户 kubectl create ns monitor-sa kubectl create serviceaccount monitor -n monitor-sa kubectl create clusterrolebinding monitor-clusterrolebinding -n monitor-sa --clusterrolecluster-admin --serviceaccountmonitor-sa:monitor 创建…

视网膜长尾数据

视网膜长尾数据 问题:视网膜疾病分类,解法:深度学习模型问题:数据复杂性处理,解法:多任务框架(同时处理多种疾病)和少量样本学习(提高对罕见疾病的识别)问题&…

找不到MSVCR120.dll,缺失MSVCR120.dll的多种解决方法

当计算机系统在运行过程中无法找到MSVCR120.dll这个特定的动态链接库文件时,可能会引发一系列问题和异常情况。首先,这可能导致某些应用程序无法正常启动或运行,因为MSVCR120.dll是许多基于Microsoft Visual C编译的应用程序所必需的核心组件…

Inventor 2024下载安装教程,免费使用,附安装包和工具,流程简单,小白也能轻松搞定

前言 Inventor是一款专业的三维可视化实体建模软件,Inventor.主要用于各类二维机械制图、三维制图的设计和开发等操作,可以广泛地应用于零件设计、钣金设计、装配设计等领域。 准备工作 1、Win7及以上系统 2、提前准备好 Inventor 2024 安装包 没有…

开源项目TARZAN-NAV | 基于springboot的现代化导航网站系统

TARZAN-NAV 导航网站 一个基于 Spring Boot、MyBatis-Plus、h2database、ehcache、Docker、websocket等技术栈实现的导航网站系统,采用主流的互联网技术架构、全新的UI设计、支持一键源码部署,拥有完整的仪表板、导航管理,用户管理、评论管理…

产品经理的发展方向是什么?市场需求现状如何?未来有哪些趋势?作为产品经理应该如何准备?

目录 了解产品经理的发展方向 市场需求现状 未来有那些趋势? 作为产品经理应该作何准备? 了解产品经理的发展方向 市场需求现状 未来有那些趋势? 个人软件 :智能终端,轻量化应用,虚拟社交等企业软件&#xff1a…

JAVA双列集合Map的特点

一次存一对元素,分别是 键 和 值,他们是一 一对应的:其中:键不可以重复,值可以重复这一对数据叫键值对、键值对对象、或 Entry Map 的体系结构: Map的常见API: 方式的实现:注意 Map …

ElasticSearch7.7.1集群搭建 Kibana安装

前言 Elasticsearch(ES)是一个基于Apache Lucene的分布式、高扩展、近实时的搜索引擎,主要用于海量数据快速存储、实时检索、高效分析的场景。通过简单易用的RESTful API,Elasticsearch隐藏了Lucene的复杂性,使得全文搜…

86.网游逆向分析与插件开发-物品使用-物品丢弃的逆向分析与C++代码的封装

内容参考于:易道云信息技术研究院VIP课 上一个内容:物品使用的逆向分析与C代码的封装-CSDN博客 码云地址(ui显示角色数据 分支):https://gitee.com/dye_your_fingers/sro_-ex.git 码云版本号:7563f86877c…

JVM-类的生命周期

类的生命周期概述 类的生命周期描述了一个类加载、使用、卸载的整个过程。整体可以分为: 加载 连接,其中又分为验证、准备、解析三个子阶段 初始化 使用 卸载 加载阶段 加载(Loading)阶段第一步是类加载器根据类的全限定名通过不同的渠道以二进制流的方…

c++|类和对象(下)

一、再谈构造函数 1.1初始化列表 在上一章节中,对于类我们可以形象的比喻为房子的图纸,而真正对于类的初始化可以比喻为建造了一个实体房子,即创建对象,对于房子中的各个房间都有特定的位置构造,那么对于类中的成员变…

一张图搞清楚HTTP状态码

HTTP状态码的基本概念 在客户端和服务器连接交互的时候,一般是客户端先给服务器发送请求,然后服务器返回结果。客户端和服务器之间的交互非常频繁,涉及到很多种不同类型的操作,大多数的时候服务器能成功返回结果,有时…

DC-磁盘配额(23国赛真题)

2023全国职业院校技能大赛网络系统管理赛项–模块B:服务部署(WindowServer2022) 文章目录 DC-磁盘配额题目配置步骤验证查看DC2驱动器C:\的磁盘配额,限制磁盘空间,警告等级等配置 DC-磁盘配额 题目 在DC2驱动器C:\上…

全新魅思V20正规视频影视系统源码/APP+H5视频影视源码

全新魅思V20正规视频影视系统源码,APPH5视频影视源码。会员花费三千购入的,具体搭建教程放压缩包了! 有兴趣的下载自行研究吧,搭建一共要用到3个域名,可以拿二级域名搭建。

PMP重考流程与费用

很多参加PMP考试的考生都经历过辛勤的学习过程,特别是那些在毕业几年后才开始备考的人。对大多数人来说,PMP考试都是一项艰难的任务。尽管PMP考试的平均通过率超过90%,但仍然有些人无法在首次尝试中通过考试。那么,如果一次没有通…

Linux ---- Shell编程之正则表达式

一、正则表达式 ​ 由一类特殊字符及文本字符所编写的模式,其中有些字符(元字符)不表示字符字面意义,而表示控制或通配的功能,类似于增强版的通配符功能,但与通配符不同,通配符功能是用…

Apache POI 处理excel文件 记录用法

Apache POI 写excel public static void write() throws IOException {//再内存中创建了一个Excel文件XSSFWorkbook excel new XSSFWorkbook();//创建一个sheet页XSSFSheet sheet excel.createSheet("info");//这里创建行对象,这里的rownum 是从0开始的,类似于数…

Redis学习——高级篇①

Redis学习——高级篇① Redis7高级之单线程和多线程(一) 一、Redis单线程VS多线程1.Redis的单线程部分1.1 Redis为什么是单线程?1.2 Redis所谓的“单线程”1.3 Redis演进变化1.3.1 Redis 3.x 单线程时代性能很快的原因1.3.2…

Python处理图片生成天际线(2024.1.29)

1、天际线简介 天际线(SkyLine)顾名思义就是天空与地面的边界线,人站在不同的高度,会看到不同的景色和地平线,天空与地面建筑物分离的标记线,不得不说,每天抬头仰望天空,相信大家都可…

窥探向量乘矩阵的存内计算原理—基于向量乘矩阵的存内计算

在当今计算领域中,存内计算技术凭借其出色的向量乘矩阵操作效能引起了广泛关注。本文将深入研究基于向量乘矩阵的存内计算原理,并探讨几个引人注目的代表性工作,如DPE、ISAAC、PRIME等,它们在神经网络和图计算应用中表现出色&…