一个看似很简单的redis需求,我问了农行和腾讯大佬也没想出更优秀的解决方案!

欢迎关注,微信公众号/知乎/稀土掘金/小红书都叫 ------【浣熊say】

专注输出国央企招聘、Java开发、物联网和数字孪生相关优质内容,关注公众号有小福利哦~

一个看似很简单的redis需求,我问了农行和腾讯大佬也没想出更优秀的解决方案!

来自领导的需求?

最近小浣熊领导又作妖了,给了我一个乍一听很简单,但是我想了半个小时也没想出更好解决方案的需求。

背景是这样的,我们有一个MQTT集群,其中有一个topic是某类IOT数据,也就是某个设备的"胎压",这个数据需要实时通过websock推送到前端,并且有个参数需要计算两次传递值的差值。

本来这块儿当时是让外包兄弟做的,领导也没把需求定义清楚,外包兄弟很直接,直接给我弄了上一秒的"胎压"和这一秒的"胎压"存在redis里面,算这个差值的时候直接取上一秒的"胎压"和本次的"胎压"一算就完事儿了,代码如下:

scss 复制代码
    private void redisCacheTirePressure(String data) {

        try {
            // generate cache key
            String redisKey = RopewayUtils.GetRopewayCaheKey(MongoConstants.TIRE_PRESSURE_LIST_CACHEKEY);
            // put new value
            redisTemplate.opsForList().rightPush(redisKey, data);

            // get new total size
            Long size = redisTemplate.opsForList().size(redisKey);

            if (size.intValue() > 2) {
                // remove other record(s), just keep two records
                redisTemplate.opsForList().trim(redisKey, size - 2, size - 1);
            }

        } catch (Exception e) {
//            e.printStackTrace();
        }

    }

好家伙,我真想反手给外包兄弟一个赞👍,代码注释还是英文,很有水平。但是,领导最近看了看,发现不对啊,为啥这个"胎压"的差值一直没变化 啊,半个月过去了气都漏完了,胎压差值还是0,肯定是小浣熊又写bug了。

我特么是诚惶诚恐,立马停止摸鱼,开始想起了解决方案。

农行、腾讯大佬怎么说?

想了半天,小浣熊想了个效率不怎么高的方案,就是把半个小时的历史数据全部缓存在redis里面,然后当这一秒的数据到了,我就去redis里面拿半小时前的数据,然后计算这个差值,返回给前端。

但是,这样的设计毕竟很暴力,也很不优雅,因为我们的IOT数据是1s一次的,对于一个IOT场景来说redis需要存储的数据就是30x60=1800条数据。虽然看起来还好,但是未来IOT场景增大的话,这段代码就可能存在性能问题,并且如果需求改了,计算1个小时或者24个小时甚至一个月的差值,那这个方案又当如何应对?所以小浣熊得想办法优化,就是有没有那种只存2条数据的办法?

于是自己想不出方案就找外援吧,这是就想起了自己还在腾讯呆着的研究生室友,先问问他知道怎么做不?首先咱们把需求给他描述清楚咯,入下:

腾讯大佬怎么说

我和我的室友合计了一下,好像也没想出啥好的方案~ 都在感叹自己是菜鸡,当然我更菜一点儿。

农行大佬怎么说

农行大佬的角度比较清奇,不然不能改变方案,那就改变领导,不得不说也是一个好的思路~

小浣熊的菜鸡解决方案

没办法,只有蛮干了,那我们只有在redis当中把前半个小时的历史数据缓存下来,实时删除多余的部分数据,代码如下:

scss 复制代码
    private void redisCacheTirePressure(String data,int cacheTimeMinutes) {
        try {
        
            String redisKey = RopewayUtils.GetRopewayCaheKey(MongoConstants.TIRE_PRESSURE_ZSET_CACHEKEY);
            
            long currentTime = System.currentTimeMillis();
            
            //存储当前的数据到redis的zset当中,score为当前时间,zset默认排序是按照升序排列的
            redisTemplate.opsForZSet().add(redisKey,data,currentTime);
            
            //从前到后遍历zset,找到和currentTime差值为cacheTimeMinutes的节点
            Set<Object> expiredMembers = redisTemplate.opsForZSet().rangeByScore(redisKey, Double.NEGATIVE_INFINITY, currentTime - cacheTimeMinutes * 60 * 1000);
            
            //删除超过cacheTimeMinutes的节点,保证在zset的首尾节点时差为30分钟
            for (Object expiredMember : expiredMembers) {
            
                redisTemplate.opsForZSet().remove(redisKey, expiredMember);
                
            }
            
        } catch (Exception e) {
        
            logger.error(e.getMessage());
            
        }
    }

计算实时差值数据的时候,只需要取redis中zset的第一个和最后一个元素进行计算就可以咯,需求就这样完成了!

ini 复制代码
        
        String redisKey = RopewayUtils.GetRopewayCaheKey(MongoConstants.TIRE_PRESSURE_ZSET_CACHEKEY);
        
        Set<String> records = redisTemplate.opsForZSet().range(redisKey,0,-1);
        
        List<String> recordsList = new ArrayList<>(records);
        
        if (recordsList != null && records.size() > 1) {
            String prev = recordsList.get(0);
            String next = recordsList.get(recordsList.size() - 1);
            //其它的操作
        }
        

最后

当然,我这个方案漏洞百出,首先当项目规模扩大之后,就算一个IOT设备那么数据也有30x60=1800条,如果设备数据不断扩张,极有可能干爆redis。并且,假设当领导改成计算一个月的差值的话,那么数据量将会达到60x60x24x30 = 259万条的zset数据,redis爆之无疑。

不知道掘金的各位大佬有没有其它的好方案,帮帮我这个菜鸡想出更好的方案,适应当项目规模扩大的时候或者领导改需求时的场景,感激不尽~

相关推荐
goTsHgo13 分钟前
在 Spring Boot 的 MVC 框架中 路径匹配的实现 详解
spring boot·后端·mvc
waicsdn_haha25 分钟前
Java/JDK下载、安装及环境配置超详细教程【Windows10、macOS和Linux图文详解】
java·运维·服务器·开发语言·windows·后端·jdk
Q_192849990635 分钟前
基于Spring Boot的摄影器材租赁回收系统
java·spring boot·后端
良许Linux40 分钟前
0.96寸OLED显示屏详解
linux·服务器·后端·互联网
求知若饥1 小时前
NestJS 项目实战-权限管理系统开发(六)
后端·node.js·nestjs
左羊1 小时前
【代码备忘录】复杂SQL写法案例(一)
后端
gb42152872 小时前
springboot中Jackson库和jsonpath库的区别和联系。
java·spring boot·后端
程序猿进阶2 小时前
深入解析 Spring WebFlux:原理与应用
java·开发语言·后端·spring·面试·架构·springboot
颜淡慕潇2 小时前
【K8S问题系列 |19 】如何解决 Pod 无法挂载 PVC问题
后端·云原生·容器·kubernetes