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 就可以发挥优势了。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于WordPress开发微信小程序1:搭建Wordpress

2年前,在知乎上提问:多数公司为什么宁愿自研也不用wordpress二次开发建站? - 知乎 (zhihu.com),收到了,很多回答 自己打算做一下提升,便有了自己基于wordpress开发微信小程序的想法 项目定位 基于wordpre…

渗透测试之信息收集下篇

华子目录 查找真实IP多地ping确认是否使用CDN查询历史DNS解析记录DNSDB微步在线Ipip.netviewdns phpinfo绕过CDN 旁站和C段站长之家google hacking网络空间搜索引擎在线C段C段利用脚本Nmap,Msscan扫描等常见端口表 网络空间搜索引擎FOFA的常用语法 扫描敏感目录/文件御剑7kbsto…

Oracle 面试题 | 08.精选Oracle高频面试题

🤍 前端开发工程师、技术日更博主、已过CET6 🍨 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 🕠 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 🍚 蓝桥云课签约作者、上架课程《Vue.js 和 E…

软考18-上午题-线性结构

一、线性结构 线性结构是一种逻辑结构。 特点:一个出度 一个入度(一个接一个排列) 1-1、线性表 最简单,最基本的线性结构。 定义:n(n > 0)个元素的有限序列。 特点: 线性表…

如何读论文

如何读论文 0. 目的 单篇文章从头读到尾,可以; 世界上那么多篇文章, 都这样读, 时间上划不来。 适合你的文章就那么一小撮。 paper 的八股文结构: titleabstractintromethodexpconclusion 1. 第一遍 海选&#…

152基于matlab的GUI滚动轴承特征频率计算

基于matlab的GUI滚动轴承特征频率计算,输入轴承参数,包括转速,节圆直径、滚子直径、滚子数、接触角,就可得滚动特征频率结果,程序已调通,可直接运行。 152 matlab 轴承特征频率 GUI (xiaohongshu.com)

代码随想录 Leetcode222.完全二叉树的节点个数

题目&#xff1a; 代码&#xff08;首刷自解 2024年1月30日&#xff09;&#xff1a; class Solution { public:int countNodes(TreeNode* root) {int res 0;if (root nullptr) return res;queue<TreeNode*> deque;TreeNode* cur root;deque.push(cur);int size 0;w…

Shopee菲律宾选品:如何找到畅销产品并提高销售业绩

在电子商务行业中&#xff0c;产品的选择是取得成功的关键之一。对于在Shopee菲律宾平台上销售的卖家来说&#xff0c;找到畅销产品是至关重要的。本文将介绍一些有效的方法&#xff0c;帮助卖家在Shopee菲律宾平台上选择热销产品&#xff0c;并提高销售业绩。 先给大家推荐一…

【Linux】理解系统中一个被打开的文件

文件系统 前言一、C语言文件接口二、系统文件接口三、文件描述符四、struct file 对象五、stdin、stdout、stderr六、文件描述符的分配规则七、重定向1. 重定向的原理2. dup23. 重谈 stderr 八、缓冲区1. 缓冲区基础2. 深入理解缓冲区3. 用户缓冲区和内核缓冲区4. FILE 前言 首…

25考研|660/880/1000/1800全年带刷计划

作为一个参加过两次研究生考试的老学姐&#xff0c;我觉得考研数学的难度完全取决于你自己 我自己就是一个很好的例子 21年数学题目是公认的简单&#xff0c;那一年考130的很多&#xff0c;但是我那一年只考了87分。但是22年又都说是有史以来最难的一年&#xff0c;和20年的难度…

Entity实体设计

Entity实体设计 &#x1f4a1;用来和数据库中的表对应&#xff0c;解决的是数据格式在Java和数据库间的转换。 &#xff08;一&#xff09;设计思想 数据库Java表类行对象字段&#xff08;列&#xff09;属性 &#xff08;二&#xff09;实体Entity编程 编码规范 &#x1f4a…

谈谈BlueStore

目录 未完待续前言组成前期准备工作基础概念对象PextentextentBlobNode 线程事务磁盘的抽象与分配位图法分层位图 上电流程写流程读流程参考资料 未完待续 前言 BlueStore是什么&#xff1f; Ceph是一个统一的分布式存储系统。BlueStore是Ceph的存储引擎。 它的作用是什么&am…

【Go语言成长之路】引入外部包

文章目录 引入外部包一、查找需要引用的包二、引入需要导入的包三、运行程序 引入外部包 ​ 实现Demo: 引用rsc.io/quote包来实现打印输出 一、查找需要引用的包 ​ 比如说我现在想要找一个quote的包&#xff0c;那么可以通过如下步骤进行操作&#xff1a; 访问pkg.go.dev,并…

2024美赛数学建模E题思路分析 - 财产保险的可持续性

1 赛题 问题E&#xff1a;财产保险的可持续性 极端天气事件正成为财产所有者和保险公司面临的危机。“近年来&#xff0c;世界已经遭受了1000多起极端天气事件造成的超过1万亿美元的损失”。[1]2022年&#xff0c;保险业的自然灾害索赔人数“比30年的平均水平增加了115%”。[…

【Spring源码分析】推断构造方法

推断构造方法源码解析 一、确认候选构造——AutowireAnnotationBeanPostProcessor#determineCandidateConstructors二、autowireConstructor 方法源码解析三、总结 阅读此需阅读下面这些博客先【Spring源码分析】Bean的元数据和一些Spring的工具【Spring源码分析】BeanFactory系…

基于大数据的淘宝电子产品数据分析的设计与实现

&#xff08;1&#xff09;本次针对开发设计系统并设置了相关的实施方案&#xff0c;利用完整的软件开发流程进行分析&#xff0c;完成了设置不同用户的操作权限和相关功能模块的开发&#xff0c;最后对系统进行测试。 &#xff08;2&#xff09;框架可以帮助程序开发者快速构建…

Gson源码解读

一&#xff0c;概述 gson作为流行的json工具&#xff0c;笔者使用较多。本文主要目的是解读下Gson的源码实现&#xff0c;就没有然后了。 二&#xff0c;实例 实例如下图所示&#xff0c;笔者简单调用gson的toJson方法获得json字符串&#xff0c;fromJson则从json字符串解析…

Cmake语法学习2:常用变量

目录 1.常用变量简介 1.1提供信息的变量 1.2改变行为的变量 1.3描述系统的变量 ​编辑1.4控制编译的变量 2.提供信息的变量 2.1PROJECT_SOURCE_DIR 和 PROJECT_BINARY_DIR 2.2 CMAKE_SOURCE_DIR 和 CMAKE_BINARY_DIR 2.3CMAKE_CURRENT_SOURCE_DIR 和CMAKE_CURRENT_BIN…

【文件上传WAF绕过】<?绕过、.htaccess木马、.php绕过

&#x1f36c; 博主介绍&#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 hacker-routing &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【应急响应】 【python】 【VulnHub靶场复现】【面试分析】 &#x1f389;点赞➕评论➕收藏…

【笔记】React Native实战练习(仿网易云游戏网页移动端)

/** * 如果系统看一遍RN相关官方文档&#xff0c;可能很快就忘记了。一味看文档也很枯燥无味&#xff0c; * 于是大概看了关键文档后&#xff0c;想着直接开发一个Demo出来&#xff0c;边学边写&#xff0c;对往后工作 * 开发衔接上能够更顺。这期间肯定会遇到各种各样的问题&a…