记一次线上严重并发bug

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
简介: 记一次线上严重并发bug

记一次线上并发bug




前言


最近在写一个视频会议系统,我这边主要的一部分代码是RTC回调,还有一部分是客户端回调。在处理回调的时候都是按照同步的方式去进行的,本质上对于我来说不能有并发问题的,但是它确实发生了,那么我们一起来看下。


展示问题代码


下面这段代码主要含义就是当客户端开启共享屏幕或者视频的时候需要客户端回调给服务端RTC状态,服务端需要根据RTC状态计算最新视频画面排序规则。


// 1+6 1:主窗口一个 6 右侧小窗口数量
    public List<Integer> getTop7(String roomId, Integer mainN, Integer rightN) {
        // 【redis:zset】获取top7排序
        List<Integer> topList = getTopN(roomId, mainN, rightN);
        log.info("Top7返回的排序列表:"+topList);
        // 【redis:list】主要针对右侧小窗口缓存 减少相对调换次数
        List<Integer> lastList = getLastScreenSortList(roomId, "7");
        log.info("Top7最后一次排序:"+lastList);
        List<Integer> tmpLastList = new ArrayList<>(lastList);
        List<Integer> tmpTopList = new ArrayList<>(topList);
        // 纯算法计算稳定性排序 不涉及中间件存储和读取
        List<Integer> newList = updateLastScreenSortList(tmpLastList, tmpTopList);
        log.info("Top7当前最新排序:"+newList);
        // 【redis:list】删除最后一次排序key
        deleteLastScreenSortList(roomId);
        if (newList.size() > 0) {
            // 【redis:list】存储最新排序为最后一次排序key
            saveLastScreenSortList(roomId, newList, "7");
        }
        log.info("Top7当前最新排序:"+newList);
        return newList;
    }


大家可以看下,并发问题主要会出现在代码块哪里???



问题就是出现在如下代码块中:


// ---并发代码开始---
    deleteLastScreenSortList(roomId);
    if (newList.size() > 0) {
        // 【redis】存储最新排序为最后一次排序key
        saveLastScreenSortList(roomId, newList, "7");
    }
    // ---并发代码结束---
    // 保存最后一次排序列表
    public void saveLastScreenSortList(String roomId, List<Integer> userIds, String topN) {
        redisTemplate.opsForList().rightPushAll(LAST_SCREEN_SORT_KEY.setParamValue(topN, roomId), userIds);
    }


为什么会出现并发问题呢?就是当出现多个RTC回调同时走到并发代码段开始的时候,就会出现一个现象,这个现象就是:删删增增也就说多个请求会同时执行删除操作,然后同时走到存储部分。而存储部分是调用redis的list结构的rightPushAll方法,因此会push多份数据,那如果有大量上万级别回调同时进行呢?那就会导致数据在短时间内急剧膨胀,导致redis内存告警,当前服务和其他服务不可用。

这是非常严重的一个并发操作不当导致的线上问题,因此需要大家特别留心观察自己的代码,尤其是共享资源部分。


解决方案


  1. 可以用并发锁解决
  2. 可以用lua 脚本
  3. 可以用redis事务


这几种方案都可以,就看自己的场景适合哪一种,我们选择第二种方式,保证删除和新增都是原子操作。当然第三种也可以,只要保证删和增命令没问题就可以。至于第一种锁的粒度需要掌控好,同时也要保证锁的正确释放。

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
6月前
|
测试技术
线上问题,如何处理?
线上问题,如何处理?
166 37
|
3月前
|
消息中间件 Java 调度
一次线上服务CPU100%的排查过程
文章记录了一次线上服务CPU使用率达到100%的排查过程,通过使用top命令和jstack工具确定了导致高CPU使用的线程,并分析了Disruptor组件的不当配置是问题原因,通过修改组件的策略成功解决了问题。
75 0
|
4月前
|
Java BI 运维
开发与运维配置问题之升级机器配置后出现频繁的GC问题和超长的GC时间如何解决
开发与运维配置问题之升级机器配置后出现频繁的GC问题和超长的GC时间如何解决
35 1
|
6月前
|
SQL 监控 数据库
线上服务假死排查
线上服务假死排查
47 0
|
11月前
|
前端开发 Cloud Native 大数据
坑爹,线上同步近 3w 个用户导致链路阻塞引入发的线上问题,你经历过吗?
坑爹,线上同步近 3w 个用户导致链路阻塞引入发的线上问题,你经历过吗?
|
运维 监控 前端开发
记一次线上 bug 的排查分析过程及总结
记一次线上 bug 的排查分析过程及总结
记一次线上 bug 的排查分析过程及总结
|
SQL canal 运维
JVM第六讲:线上环境 FGC 频繁,如何解决?
JVM第六讲:线上环境 FGC 频繁,如何解决?
293 0
|
存储 缓存 监控
线上服务发布抖动,该怎么解决呢
之前的文章分别讲了[优雅上线]和 [优雅下线],实际工作中做了优雅上下线后,服务发布后还是会有短暂的“抖动”,接口的响应时间急剧升高后又恢复正常,就和下面的监控图一样
158 0
线上服务发布抖动,该怎么解决呢
|
Java Linux
并发编程系列:线上问题定位
面试时经常会被问到“CPU飙高怎么排查”,“内存泄露怎么定位解决”这类问题。本篇介绍一些常见的分析方法和工具
125 0