最近遇到一个业务场景,外部接口每隔 5 秒种会给我一批温度参数, 分别为每个机器上的不同温度,需要在前台动态曲线图实时显示,前台 每次 只能 看某 1 天和某一个机器的 温度参数, 历史数据曲线图 可以查询
技术上我做了分表,每一天的数据建一张表,大概 100 台左右的机器数量,每一天的数据 172 万, 单台机器的温度数据每天大概为 1.7 万
前置条件: 任何一台机器温度数据一旦生成是不会变的,我的思路能不能放缓存中,这样就不用去 100 多万条数据,拿机器编号去匹配了
但是我尝试把 1.7 万条数据 放入 redis 中,这个时候 redis 直接报了个命令超时,应该是 value 过大,导致性能下降,IO 瓶颈太大
所以 这种场景的需求究竟要怎么处理呢? 感觉 我这个需求和股票的实时曲线很像 好难啊 呜呜呜
1
6IbA2bj5ip3tK49j 2020-11-30 17:06:41 +08:00
每台机器的 1.7 万条数据,放到一个 list/zset 里面去。
|
2
hehe12980 OP 一条一条放 取的时候 批量取么
|
4
6IbA2bj5ip3tK49j 2020-11-30 17:14:52 +08:00
@hehe12980 采集一次放一次呗。取的时候批量取。
看起来用 zset 比较合适。用生成时间做为 score,也方便清理过时数据。 |
5
Jooooooooo 2020-11-30 17:15:53 +08:00 2
大 key 简单的做法是打散
散到 100 个 sub key 里去, 然后捞这个 key 数据的时候直接 pipeline 去捞这 100 个 sub key 然后组装起来 |
6
XDJI 2020-11-30 17:19:03 +08:00
可以切下 比如机器 1 切 10 个 key 存的时候批量存 取得时候批量取就是
|
7
pushback 2020-11-30 17:21:08 +08:00
可以参考 hashmap 的切法
|
8
qwerthhusn 2020-11-30 17:23:02 +08:00
时序数据库是不是更适合。。
|
9
sagaxu 2020-11-30 17:27:04 +08:00 via Android
历史数据存 db,不用缓存,一张表存明细,一张表每行是一个设备一天所有数据。
实时数据存 hashmap,key 为设备 ID,field 为时间 slot(0 到 17280),绘制实时曲线的时候,首次取整个 hash,之后只要取比上次最大 slot 更大的值。 |
10
dynastysea 2020-11-30 17:27:16 +08:00
为什么用 redis ?你的查询量多大?
|
11
chengz 2020-11-30 17:34:16 +08:00
应该是拿 redis 当缓存用
存入可以数据可以实时存 读取数据分批量取,还有大 key 就考虑将大 key 拆分 |
12
lyy16384 2020-11-30 17:46:58 +08:00
你的机器编号没索引的吗
|
14
hehe12980 OP @dynastysea 没啥查询量 就是历史数据 去数据库查 感觉慢, 历史数据是固定的
|
15
rrfeng 2020-11-30 18:01:31 +08:00
建议上个时间序列数据库,influxdb 或者直接上个 prometheus (如果不要求持久化的话)
|
16
rrfeng 2020-11-30 18:02:23 +08:00
这数据量对专业的 TSDB 来说是小儿科~
|
18
rrfeng 2020-11-30 18:07:13 +08:00
1.7w 个 float 也没多大啊,改一下超时时间呗
|
19
hehe12980 OP @rrfeng 不止温度一个字段,有 6 个业务字段,绘制 6 条曲线,实时的,所以放 redis 没问题,取的时候 设置 3s 超时 直接爆了 感觉 还没 mysql 快 感觉 redis 用的有问题==
|
20
hehe12980 OP @sagaxu 历史数据存 mysql, 查的时间大概是 0.5s 到 1.1 秒之间,感觉这个响应时间还是有点慢了
|
22
sagaxu 2020-11-30 18:15:05 +08:00 via Android
@hehe12980 一次取 1.7 万条数据?那为何不把这 1.7 万条放一行里面呢? mysql 也有数组或者 json 吧。
|
23
hehe12980 OP @lyy16384 去 mysql 还有通过 索引检索 但是 如果 我做一个 k-v 映射 提前放入 redis 会好点把
|
25
qq316107934 2020-11-30 18:20:02 +08:00
按照时间做分表,每小时存一个 key,然后像 1 楼说的用 list/zset,取的时候根据时间范围去批量拿,这样可以避免超时。
|
26
SlipStupig 2020-11-30 18:20:31 +08:00
你这个用法不太对,redis 单个 key 是有大小限制的,这要做非常不利于检索,机器肯定是是有标识符的,用机器名称拆分作为 namesapce,然后用小时生成一个命令空间,使用 zset 作为数据存储类型,例如:Machine1:2020-11-30:00,这个 key 代表就某一机器在 2020-11-30 的 0 点~1 点之间产生的数据,要匹配机器或者时间范围的时候,可以使用 zscan 去扫描前缀,对特定温度可以用 zscore 去检索,对于 KEY 的如果只查当天缓存,可以使用 TTL 值过期,如果性能压力特别大,就需要自己实现一个 TTL 机制去销毁 key 了
|
27
optional 2020-11-30 18:30:11 +08:00 via iPhone
这个需求直接存数据库加索引就行,没必要 redis,redis 不适合批量取。
|
28
lyy16384 2020-11-30 18:58:04 +08:00
@hehe12980 #23 而且只有整页刷新的时候才会查全天的数据,实时更新曲线只要查增量数据,用数据库完全不会成为性能瓶颈
|
29
614457662 2020-11-30 19:46:24 +08:00 via Android
时序数据库或者 elastic search 吧,现成的轮子用着方便快捷。
|
30
dynastysea 2020-11-30 20:24:29 +08:00
@hehe12980 我晕,一堆人也是需求没搞清楚就乱出主意,没啥查询量你告诉我用 redis 干什么? redis 解决什么问题??
|
31
4771314 2020-11-30 20:28:55 +08:00
redis 不是这样用的吧 QAQ
直接时序数据库吧,场景很合适 |
32
funky 2020-11-30 20:29:07 +08:00
timescladb
|
33
ixiaohei 2020-11-30 20:30:05 +08:00
用时序数据库吧,influxdb 你值得了解一下
|
34
makdon 2020-11-30 20:40:13 +08:00
可以了解下 Apache 的 druid,这种场景很适合
|
35
rrfeng 2020-11-30 20:41:05 +08:00
你非要用 mysql 也可以,我给你造个方案,但是估计你不会想采用:
首先如上面某同学所说,这场景根本不用 Redis,因为你每次都是取全量数据,根本没有冷热之分,而且 Redis 并发支持还有问题,不如直接从 mysql 里取。 那么现在的问题就变成了怎么从 mysql 取最快:压缩。 可以一张表暂存 push 上来的 1 小时内的数据,在某小时过去 x 分钟之后,前一小时的数据可以认为已经固化了,启动个后台线程压缩存到另一张表里。 压缩算法用什么呢?抄一下 prometheus 或者相关衍生产品的压缩算法(专门针对大量 float ),大概可以做到 1-2Byte 每个数字。这样每小时只有 1MB 的数据。 |
36
kingfalse 2020-11-30 21:13:12 +08:00 via Android
字段太大的话可以先压缩一下,再放,gzip 一下,效果很明显
|
37
iyaozhen 2020-11-30 21:26:16 +08:00
我之前是历史的存 MySQL,redis 只存最近几小时的。用 unix 时间戳做 key,反正几小时多少秒是固定的,批量拿 key 就行
|
38
hehe12980 OP @dynastysea 难道 redis 只能解决,查询量大的需求,如果 我有一个查询效率不是很高的 sql (出于业务场景可能要连表查询或者数据量太大), 如果结果是固定不变的冷数据,为什么不能放缓存呢?
|
39
MineDog 2020-11-30 21:54:37 +08:00
我觉得时序数据库更符合你的要求
|
40
liuhuansir 2020-12-01 00:19:17 +08:00 via Android
opentsdb
|
41
akira 2020-12-01 01:02:48 +08:00
influxdb + prometheus
|
42
no1xsyzy 2020-12-01 01:49:08 +08:00
你这是把 redis 当 kv store 了?
|
43
no1xsyzy 2020-12-01 01:51:57 +08:00
我跟你说,你只是作一个复杂 query 的缓存的话,取巧的,直接拿文件做
|
44
noparking188 2020-12-01 07:05:13 +08:00
我咋感觉分表分得更难做了,也许应该换个分表方式
|
45
noqwerty 2020-12-01 08:31:25 +08:00 via Android
这两天刚好在玩类似的东西,influxdb+grafana 完美契合
|
46
Yuansir 2020-12-01 08:36:06 +08:00
这应该是时序数据干的活
|
47
jhhhh 2020-12-01 09:08:35 +08:00
redis 不合适,mysql 不想用。
那可以尝试下文件方式。按你后续要搜索的条件去存储响应格式内容的文件。 |
48
xuanbg 2020-12-01 09:24:20 +08:00
这个需求不适合用 redis,内存会炸。influxdb 还是比较合适的。
问题是你画曲线直接用 5 秒的原始数据?这 x 轴 17280 个坐标点?什么显示器能把 1 天的数据给显示全了,18K 么??? |
50
hehe12980 OP @xuanbg 像股票的曲线图是秒级的 一天差不多也 1 万多个点 它们的曲线图 不就显示全了,只要曲线很平滑就能显示的很舒服
|
51
fengpan567 2020-12-01 09:58:23 +08:00
K 线图都没你这数据量大,时间周期短的也是 1 分钟
|
52
Mithril 2020-12-01 10:01:32 +08:00
直接上时序数据库。懒得折腾 influxdb 的花,直接 prometheus 。
连代码都不用写。。 |
53
dynastysea 2020-12-01 10:04:34 +08:00
@hehe12980 因为从你的场景出发,这个是完全没必要的,你说的这个 sql 完全就可以满足,没必要提早过度优化。
|
54
stevenkang 2020-12-01 10:09:05 +08:00
5 秒钟上报一次,按天的维度下,并不需要 5 秒钟刷新一次前端展示。
按天算,24 小时 x 60 分钟 = 1440,一分钟刷新一次足以满足需求。多了也看不出来有啥变化。 按小时算,60 分钟 x 60 秒 = 3600,算一半 1800,两秒钟刷新一次也满足需求,多了同样看不出来啥变化。 总之,不要在一个时间范围很大的维度上,展示的数据粒度又很小,那样无意义(参考各种监控系统的设计)。 |
55
xiaofan2 2020-12-01 10:09:13 +08:00
上面说的时序数据库完全可以 目前我们公司运维用的是 clicckhouse,这个感觉应该也可以,但我还没深入了解过,你可以了解下
|
56
hehe12980 OP @fengpan567 K 线图是分钟级别的么 那比如一分钟 60 个价格的点 K 线图的价格 怎么变成一个点 取平均值?
|
57
Garland 2020-12-01 11:13:48 +08:00
唔,这个是时序数据库的标准场景了。
|
58
Jrue0011 2020-12-01 11:24:02 +08:00
哈哈哈所以这算是一个经典 XY 问题吗,本身要解决的不是 redis 存大 value,甚至都不是大量数据的查询优化,而是本身这个数据展示的需求设计
|
59
muskill 2020-12-01 11:28:10 +08:00
influxdb 合适
|
61
hehe12980 OP @stevenkang 那历史的数据 比如 17200 多个点 汇成曲线 展示的话 不全拿出来 那曲线图是不是就不对了 从某种意义上来说 实时数据 可以按你说的 那样干 历史数据 要怎么 拿出来展示比较合理呢
|
62
BadAngel 2020-12-01 11:40:59 +08:00
Redis 要解决的问题是快。
一次查询,缓存一天所有设备数据数据,下一次查询把之前的缓存删掉,又缓存另外一天?和查数据库没有区别吧。 除非你缓存全量数据,否则解决不了你的需求。 |
63
neptuno 2020-12-01 11:43:18 +08:00
1.精简数据结构,1.7 万条数据尽量精简到时间-温度两个纬度
2.你用 redis 和用 influxdb 都是引入第三方,为啥不一步到位用 influxdb,正好学学新知识嘛 |
65
9684xtpa 2020-12-01 12:29:50 +08:00
zset,取得时候分页取就好了,按天做 KEY,我们以前一个 ZSET 存放一百多万条的 value 一点问题都没有
|
67
kvkboy 2020-12-01 14:02:15 +08:00
你可以了解下 RedisTimeSeries,是 Redis 的一个扩展模块。
专门面向时间序列数据 |
68
luwill 2020-12-01 14:07:15 +08:00
如果一定要用 mysql 。合并数据:将一定的区间段( 10 分钟 120 条)的数据合并,压缩存入 blob 列。并计算存储平均值,用于粗粒度查询。
这样一天 144 条,根据前端选择范围返回粗或细粒度数据。粗粒度使用平均数,细粒度解压 blob 展开。 |
69
bugmakerxs 2020-12-01 14:26:34 +08:00
@hehe12980 『如果结果是固定不变的冷数据,为什么不能放缓存呢?』因为内存 /三级缓存很贵,所以只有少量热数据才会放缓存。计算机原理如是说。
|
70
hehe12980 OP @bugmakerxs 额 好吧 我错了 这思路却是不对
|
71
remarrexxar 2020-12-01 15:49:09 +08:00
如果是写入后不修改只查询的话落入文件就行了吧,机器 id+日期作为文件名。数据进来先进 redis,每 N 分钟把前 N 分钟的数据写进文件。查询过去记录直接读文件,当日的综合 redis 中记录。
|
72
ggabc 2020-12-01 15:58:50 +08:00
redis 里只存最新,历史数据继续用数据库吧
|