一个看似很简单的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爆之无疑。

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

相关推荐
我命由我123455 分钟前
Java 泛型 - Java 泛型通配符(上界通配符、下界通配符、无界通配符、PECS 原则)
java·开发语言·后端·java-ee·intellij-idea·idea·intellij idea
szhf785 分钟前
SpringBoot Test详解
spring boot·后端·log4j
无尽的沉默6 分钟前
SpringBoot整合Redis
spring boot·redis·后端
摸鱼的春哥12 分钟前
春哥的Agent通关秘籍07:5分钟实现文件归类助手【实战】
前端·javascript·后端
Victor35629 分钟前
MongoDB(2)MongoDB与传统关系型数据库的主要区别是什么?
后端
JaguarJack29 分钟前
PHP 应用遭遇 DDoS 攻击时会发生什么 从入门到进阶的防护指南
后端·php·服务端
BingoGo30 分钟前
PHP 应用遭遇 DDoS 攻击时会发生什么 从入门到进阶的防护指南
后端
Victor35631 分钟前
MongoDB(3)什么是文档(Document)?
后端
牛奔3 小时前
Go 如何避免频繁抢占?
开发语言·后端·golang
想用offer打牌7 小时前
MCP (Model Context Protocol) 技术理解 - 第二篇
后端·aigc·mcp