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

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

相关推荐
Vitalia4 小时前
从零开始学Rust:枚举(enum)与模式匹配核心机制
开发语言·后端·rust
飞飞翼5 小时前
python-flask
后端·python·flask
草捏子6 小时前
最终一致性避坑指南:小白也能看懂的分布式系统生存法则
后端
一个public的class6 小时前
什么是 Java 泛型
java·开发语言·后端
头孢头孢7 小时前
k8s常用总结
运维·后端·k8s
TheITSea8 小时前
后端开发 SpringBoot 工程模板
spring boot·后端
Asthenia04128 小时前
编译原理中的词法分析器:从文本到符号的桥梁
后端
Asthenia04128 小时前
用RocketMQ和MyBatis实现下单-减库存-扣钱的事务一致性
后端
Pasregret8 小时前
04-深入解析 Spring 事务管理原理及源码
java·数据库·后端·spring·oracle
Micro麦可乐8 小时前
最新Spring Security实战教程(七)方法级安全控制@PreAuthorize注解的灵活运用
java·spring boot·后端·spring·intellij-idea·spring security