技术求助帖,关于 redis 大 value 存储的问题 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
请不要在回答技术问题时复制粘贴 AI 生成的内容
hehe12980
V2EX    程序员

技术求助帖,关于 redis 大 value 存储的问题

  • &nsp;
  •   hehe12980 2020-11-30 16:41:50 +08:00 6444 次点击
    这是一个创建于 1824 天前的主题,其中的信息可能已经有所发展或是发生改变。

    这是我在 V2EX 的第一个帖子

    最近遇到一个业务场景,外部接口每隔 5 秒种会给我一批温度参数, 分别为每个机器上的不同温度,需要在前台动态曲线图实时显示,前台 每次 只能 看某 1 天和某一个机器的 温度参数, 历史数据曲线图 可以查询

    技术上我做了分表,每一天的数据建一张表,大概 100 台左右的机器数量,每一天的数据 172 万, 单台机器的温度数据每天大概为 1.7 万

    前置条件: 任何一台机器温度数据一旦生成是不会变的,我的思路能不能放缓存中,这样就不用去 100 多万条数据,拿机器编号去匹配了

    但是我尝试把 1.7 万条数据 放入 redis 中,这个时候 redis 直接报了个命令超时,应该是 value 过大,导致性能下降,IO 瓶颈太大

    所以 这种场景的需求究竟要怎么处理呢? 感觉 我这个需求和股票的实时曲线很像 好难啊 呜呜呜

    第 1 条附言    2020-12-01 09:47:42 +08:00
    感谢大家的一些建议,第一次 V2EX 发帖,受宠若惊,V2EX 果然很程序员
    谢谢大家
    第 2 条附言    2020-12-01 17:37:22 +08:00
    我尝试了 使用文件的方式,因为技术上代价最小,很快,文件一旦生成,通过 json 反序列化能基本能在 100ms 内,把所有的数据整出来, 剩下的时间基本就花在网络传输上了,但是后续我会研究研究大家说的时序数据库,你们的建议对我的帮助很大,谢谢你们
    73 条回复    2020-12-01 16:32:58 +08:00
    6IbA2bj5ip3tK49j
        1
    6IbA2bj5ip3tK49j  
       2020-11-30 17:06:41 +08:00
    每台机器的 1.7 万条数据,放到一个 list/zset 里面去。
    hehe12980
        2
    hehe12980  
    OP
       2020-11-30 17:09:07 +08:00
    一条一条放 取的时候 批量取么
    hehe12980
        3
    hehe12980  
    OP
       2020-11-30 17:09:32 +08:00
    @xgfan 一条一条放 取的时候 批量取么
    6IbA2bj5ip3tK49j
        4
    6IbA2bj5ip3tK49j  
       2020-11-30 17:14:52 +08:00
    @hehe12980 采集一次放一次呗。取的时候批量取。
    看起来用 zset 比较合适。用生成时间做为 score,也方便清理过时数据。
    Jooooooooo
        5
    Jooooooooo  
       2020-11-30 17:15:53 +08:00   2
    大 key 简单的做法是打散

    散到 100 个 sub key 里去, 然后捞这个 key 数据的时候直接 pipeline 去捞这 100 个 sub key 然后组装起来
    XDJI
        6
    XDJI  
       2020-11-30 17:19:03 +08:00
    可以切下 比如机器 1 切 10 个 key 存的时候批量存 取得时候批量取就是
    pushback
        7
    pushback  
       2020-11-30 17:21:08 +08:00
    可以参考 hashmap 的切法
    qwerthhusn
        8
    qwerthhusn  
       2020-11-30 17:23:02 +08:00
    时序数据库是不是更适合。。
    sagaxu
        9
    sagaxu  
       2020-11-30 17:27:04 +08:00 via Android
    历史数据存 db,不用缓存,一张表存明细,一张表每行是一个设备一天所有数据。

    实时数据存 hashmap,key 为设备 ID,field 为时间 slot(0 到 17280),绘制实时曲线的时候,首次取整个 hash,之后只要取比上次最大 slot 更大的值。
    dynastysea
        10
    dynastysea  
       2020-11-30 17:27:16 +08:00
    为什么用 redis ?你的查询量多大?
    chengz
        11
    chengz  
       2020-11-30 17:34:16 +08:00
    应该是拿 redis 当缓存用
    存入可以数据可以实时存
    读取数据分批量取,还有大 key 就考虑将大 key 拆分
    lyy16384
        12
    lyy16384  
       2020-11-30 17:46:58 +08:00
    你的机器编号没索引的吗
    hehe12980
        13
    hehe12980  
    OP
       2020-11-30 17:58:00 +08:00
    @lyy16384 有索引你也是得扫 1.7 万啊
    hehe12980
        14
    hehe12980  
    OP
       2020-11-30 17:59:00 +08:00
    @dynastysea 没啥查询量 就是历史数据 去数据库查 感觉慢, 历史数据是固定的
    rrfeng
        15
    rrfeng  
       2020-11-30 18:01:31 +08:00
    建议上个时间序列数据库,influxdb 或者直接上个 prometheus (如果不要求持久化的话)
    rrfeng
        16
    rrfeng  
       2020-11-30 18:02:23 +08:00
    这数据量对专业的 TSDB 来说是小儿科~
    hehe12980
        17
    hehe12980  
    OP
       2020-11-30 18:05:25 +08:00
    @rrfeng 上新数据库应该不考虑了 毕竟引一个数据库 就干这么一个事 ==
    rrfeng
        18
    rrfeng  
       2020-11-30 18:07:13 +08:00
    1.7w 个 float 也没多大啊,改一下超时时间呗
    hehe12980
        19
    hehe12980  
    OP
       2020-11-30 18:09:58 +08:00
    @rrfeng 不止温度一个字段,有 6 个业务字段,绘制 6 条曲线,实时的,所以放 redis 没问题,取的时候 设置 3s 超时 直接爆了 感觉 还没 mysql 快 感觉 redis 用的有问题==
    hehe12980
        20
    hehe12980  
    OP
       2020-11-30 18:11:36 +08:00
    @sagaxu 历史数据存 mysql, 查的时间大概是 0.5s 到 1.1 秒之间,感觉这个响应时间还是有点慢了
    lyy16384
        21
    lyy16384  
       2020-11-30 18:12:30 +08:00
    @hehe12980 #13 这 1.7 万都是你的要取的数据,具体从 redis 取还是从数据库取根本没什么区别
    sagaxu
        22
    sagaxu  
       2020-11-30 18:15:05 +08:00 via Android
    @hehe12980 一次取 1.7 万条数据?那为何不把这 1.7 万条放一行里面呢? mysql 也有数组或者 json 吧。
    hehe12980
        23
    hehe12980  
    OP
       2020-11-30 18:16:18 +08:00
    @lyy16384 去 mysql 还有通过 索引检索 但是 如果 我做一个 k-v 映射 提前放入 redis 会好点把
    hehe12980
        24
    hehe12980  
    OP
       2020-11-30 18:17:58 +08:00
    @sagaxu 没想过 放一行的方式 感觉有点疯狂 毕竟几 M 的数据
    qq316107934
        25
    qq316107934  
       2020-11-30 18:20:02 +08:00
    按照时间做分表,每小时存一个 key,然后像 1 楼说的用 list/zset,取的时候根据时间范围去批量拿,这样可以避免超时。
    SlipStupig
        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 了
    optional
        27
    optional  
       2020-11-30 18:30:11 +08:00 via iPhone
    这个需求直接存数据库加索引就行,没必要 redis,redis 不适合批量取。
    lyy16384
        28
    lyy16384  
       2020-11-30 18:58:04 +08:00
    @hehe12980 #23 而且只有整页刷新的时候才会查全天的数据,实时更新曲线只要查增量数据,用数据库完全不会成为性能瓶颈
    614457662
        29
    614457662  
       2020-11-30 19:46:24 +08:00 via Android
    时序数据库或者 elastic search 吧,现成的轮子用着方便快捷。
    dynastysea
        30
    dynastysea  
       2020-11-30 20:24:29 +08:00
    @hehe12980 我晕,一堆人也是需求没搞清楚就乱出主意,没啥查询量你告诉我用 redis 干什么? redis 解决什么问题??
    4771314
        31
    4771314  
       2020-11-30 20:28:55 +08:00
    redis 不是这样用的吧 QAQ
    直接时序数据库吧,场景很合适
    funky
        32
    funky  
       2020-11-30 20:29:07 +08:00
    timescladb
    ixiaohei
        33
    ixiaohei  
       2020-11-30 20:30:05 +08:00
    用时序数据库吧,influxdb 你值得了解一下
    makdon
        34
    makdon  
       2020-11-30 20:40:13 +08:00
    可以了解下 Apache 的 druid,这种场景很适合
    rrfeng
        35
    rrfeng  
       2020-11-30 20:41:05 +08:00
    你非要用 mysql 也可以,我给你造个方案,但是估计你不会想采用:

    首先如上面某同学所说,这场景根本不用 Redis,因为你每次都是取全量数据,根本没有冷热之分,而且 Redis 并发支持还有问题,不如直接从 mysql 里取。

    那么现在的问题就变成了怎么从 mysql 取最快:压缩。
    可以一张表暂存 push 上来的 1 小时内的数据,在某小时过去 x 分钟之后,前一小时的数据可以认为已经固化了,启动个后台线程压缩存到另一张表里。

    压缩算法用什么呢?抄一下 prometheus 或者相关衍生产品的压缩算法(专门针对大量 float ),大概可以做到 1-2Byte 每个数字。这样每小时只有 1MB 的数据。
    kingfalse
        36
    kingfalse  
       2020-11-30 21:13:12 +08:00 via Android
    字段太大的话可以先压缩一下,再放,gzip 一下,效果很明显
    iyaozhen
        37
    iyaozhen  
       2020-11-30 21:26:16 +08:00
    我之前是历史的存 MySQL,redis 只存最近几小时的。用 unix 时间戳做 key,反正几小时多少秒是固定的,批量拿 key 就行
    hehe12980
        38
    hehe12980  
    OP
       2020-11-30 21:52:35 +08:00
    @dynastysea 难道 redis 只能解决,查询量大的需求,如果 我有一个查询效率不是很高的 sql (出于业务场景可能要连表查询或者数据量太大), 如果结果是固定不变的冷数据,为什么不能放缓存呢?
    MineDog
        39
    MineDog  
       2020-11-30 21:54:37 +08:00
    我觉得时序数据库更符合你的要求
    liuhuansir
        40
    liuhuansir  
       2020-12-01 00:19:17 +08:00 via Android
    opentsdb
    akira
        41
    akira  
       2020-12-01 01:02:48 +08:00
    influxdb + prometheus
    no1xsyzy
        42
    no1xsyzy  
       2020-12-01 01:49:08 +08:00
    你这是把 redis 当 kv store 了?
    no1xsyzy
        43
    no1xsyzy  
       2020-12-01 01:51:57 +08:00
    我跟你说,你只是作一个复杂 query 的缓存的话,取巧的,直接拿文件做
    noparking188
        44
    noparking188  
       2020-12-01 07:05:13 +08:00
    我咋感觉分表分得更难做了,也许应该换个分表方式
    noqwerty
        45
    noqwerty  
       2020-12-01 08:31:25 +08:00 via Android
    这两天刚好在玩类似的东西,influxdb+grafana 完美契合
    Yuansir
        46
    Yuansir  
       2020-12-01 08:36:06 +08:00
    这应该是时序数据干的活
    jhhhh
        47
    jhhhh  
       2020-12-01 09:08:35 +08:00
    redis 不合适,mysql 不想用。
    那可以尝试下文件方式。按你后续要搜索的条件去存储响应格式内容的文件。
    xuanbg
        48
    xuanbg  
       2020-12-01 09:24:20 +08:00
    这个需求不适合用 redis,内存会炸。influxdb 还是比较合适的。

    问题是你画曲线直接用 5 秒的原始数据?这 x 轴 17280 个坐标点?什么显示器能把 1 天的数据给显示全了,18K 么???
    hehe12980
        49
    hehe12980  
    OP
       2020-12-01 09:42:58 +08:00
    @xuanbg 那个可以曲线图可以伸缩的
    hehe12980
        50
    hehe12980  
    OP
       2020-12-01 09:45:47 +08:00
    @xuanbg 像股票的曲线图是秒级的 一天差不多也 1 万多个点 它们的曲线图 不就显示全了,只要曲线很平滑就能显示的很舒服
    fengpan567
        51
    fengpan567  
       2020-12-01 09:58:23 +08:00
    K 线图都没你这数据量大,时间周期短的也是 1 分钟
    Mithril
        52
    Mithril  
       2020-12-01 10:01:32 +08:00
    直接上时序数据库。懒得折腾 influxdb 的花,直接 prometheus 。
    连代码都不用写。。
    dynastysea
        53
    dynastysea  
       2020-12-01 10:04:34 +08:00
    @hehe12980 因为从你的场景出发,这个是完全没必要的,你说的这个 sql 完全就可以满足,没必要提早过度优化。
    stevenkang
        54
    stevenkang  
       2020-12-01 10:09:05 +08:00
    5 秒钟上报一次,按天的维度下,并不需要 5 秒钟刷新一次前端展示。

    按天算,24 小时 x 60 分钟 = 1440,一分钟刷新一次足以满足需求。多了也看不出来有啥变化。

    按小时算,60 分钟 x 60 秒 = 3600,算一半 1800,两秒钟刷新一次也满足需求,多了同样看不出来啥变化。

    总之,不要在一个时间范围很大的维度上,展示的数据粒度又很小,那样无意义(参考各种监控系统的设计)。
    xiaofan2
        55
    xiaofan2  
       2020-12-01 10:09:13 +08:00
    上面说的时序数据库完全可以 目前我们公司运维用的是 clicckhouse,这个感觉应该也可以,但我还没深入了解过,你可以了解下
    hehe12980
        56
    hehe12980  
    OP
       2020-12-01 10:18:28 +08:00
    @fengpan567 K 线图是分钟级别的么 那比如一分钟 60 个价格的点 K 线图的价格 怎么变成一个点 取平均值?
    Garland
        57
    Garland  
       2020-12-01 11:13:48 +08:00
    唔,这个是时序数据库的标准场景了。
    Jrue0011
        58
    Jrue0011  
       2020-12-01 11:24:02 +08:00
    哈哈哈所以这算是一个经典 XY 问题吗,本身要解决的不是 redis 存大 value,甚至都不是大量数据的查询优化,而是本身这个数据展示的需求设计
    muskill
        59
    muskill  
       2020-12-01 11:28:10 +08:00
    influxdb 合适
    hehe12980
        60
    hehe12980  
    OP
       2020-12-01 11:35:46 +08:00
    @Jrue0011 咋展示比较合理呢 ==
    hehe12980
        61
    hehe12980  
    OP
       2020-12-01 11:38:45 +08:00
    @stevenkang 那历史的数据 比如 17200 多个点 汇成曲线 展示的话 不全拿出来 那曲线图是不是就不对了 从某种意义上来说 实时数据 可以按你说的 那样干 历史数据 要怎么 拿出来展示比较合理呢
    BadAngel
        62
    BadAngel  
       2020-12-01 11:40:59 +08:00
    Redis 要解决的问题是快。
    一次查询,缓存一天所有设备数据数据,下一次查询把之前的缓存删掉,又缓存另外一天?和查数据库没有区别吧。
    除非你缓存全量数据,否则解决不了你的需求。
    neptuno
        63
    neptuno  
       2020-12-01 11:43:18 +08:00
    1.精简数据结构,1.7 万条数据尽量精简到时间-温度两个纬度
    2.你用 redis 和用 influxdb 都是引入第三方,为啥不一步到位用 influxdb,正好学学新知识嘛
    hehe12980
        64
    hehe12980  
    OP
       2020-12-01 11:46:38 +08:00
    @neptuno 嗯,实在不行 就整 influxdb
    9684xtpa
        65
    9684xtpa  
       2020-12-01 12:29:50 +08:00
    zset,取得时候分页取就好了,按天做 KEY,我们以前一个 ZSET 存放一百多万条的 value 一点问题都没有
    9684xtpa
        66
    9684xtpa  
       2020-12-01 12:31:58 +08:00
    @9684xtpa #65 得,直接审题就回答了,看完答案和后面楼主的回复,我这回答 0 分
    kvkboy
        67
    kvkboy  
       2020-12-01 14:02:15 +08:00
    你可以了解下 RedisTimeSeries,是 Redis 的一个扩展模块。

    专门面向时间序列数据
    luwill
        68
    luwill  
       2020-12-01 14:07:15 +08:00
    如果一定要用 mysql 。合并数据:将一定的区间段( 10 分钟 120 条)的数据合并,压缩存入 blob 列。并计算存储平均值,用于粗粒度查询。

    这样一天 144 条,根据前端选择范围返回粗或细粒度数据。粗粒度使用平均数,细粒度解压 blob 展开。
    bugmakerxs
        69
    bugmakerxs  
       2020-12-01 14:26:34 +08:00
    @hehe12980 『如果结果是固定不变的冷数据,为什么不能放缓存呢?』因为内存 /三级缓存很贵,所以只有少量热数据才会放缓存。计算机原理如是说。
    hehe12980
        70
    hehe12980  
    OP
       2020-12-01 15:30:41 +08:00
    @bugmakerxs 额 好吧 我错了 这思路却是不对
    remarrexxar
        71
    remarrexxar  
       2020-12-01 15:49:09 +08:00
    如果是写入后不修改只查询的话落入文件就行了吧,机器 id+日期作为文件名。数据进来先进 redis,每 N 分钟把前 N 分钟的数据写进文件。查询过去记录直接读文件,当日的综合 redis 中记录。
    ggabc
        72
    ggabc  
       2020-12-01 15:58:50 +08:00
    redis 里只存最新,历史数据继续用数据库吧
    GrayXu
        73
    GrayXu  
       2020-12-01 16:32:58 +08:00
    @Jrue0011 是这样 233
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2644 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 29ms UTC 12:53 PVG 20:53 LAX 04:53 JFK 07:53
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86