V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
hehe12980
V2EX  ›  程序员

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

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

    这是我在 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
    xgfan
        1
    xgfan  
       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 一条一条放 取的时候 批量取么
    xgfan
        4
    xgfan  
       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   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   1462 人在线   最高记录 5497   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 17:59 · PVG 01:59 · LAX 09:59 · JFK 12:59
    ♥ Do have faith in what you're doing.