前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >异步查询转同步加redis业务实现的BUG分享

异步查询转同步加redis业务实现的BUG分享

作者头像
FunTester
发布2020-02-17 16:08:09
5840
发布2020-02-17 16:08:09
举报
文章被收录于专栏:FunTesterFunTester

在最近的性能测试中,某一个查询接口指标不通过,开发做了N次优化,最终的优化方案如下:异步查询然后转同步,再加上redis缓存。此为背景。

在测试过程中发现一个BUG:同样的请求在第一次查询结果是OK的,但是第二次查询(理论上讲得到的缓存数据)缺失了某些字段。

后端服务的测试代码如下,代码内容作了简化,留下了关键的部分,doSomething(dataMap);为简化方法,其中teacherPadAsyncService.doExcuteLikeSateAsync()teacherPadAsyncService.doExcuteAccuracyAsync()teacherPadAsyncService.doExcuteTeacherTagAsync这三个是异步方法:

代码语言:javascript
复制
 @Override
    public void doExecute(Map<String, Object> dataMap) {
        String cache = defaultRedisUtil.getString(RedisKeyConfig.COURSE_PKG_DETAIL_KEY + id);
        if (StringUtils.isNotBlank(cache)) {
            dataMap = JSON.parseObject(cache, Map.class);
            return;
        }
        doSomething(dataMap);
        CountDownLatch countDownLatch = new CountDownLatch(3);
        String traceKey = TraceKeyHolder.getTraceKey();
        teacherPadAsyncService.doExcuteLikeSateAsync(dataMap, coursePackage.getId(),ResourceTypeEnum.COURSE_PACKAGE.value, currentUser.getSystemId(), countDownLatch, traceKey);
        teacherPadAsyncService.doExcuteAccuracyAsync(dataMap, coursePackage.getId(), countDownLatch, traceKey);
        teacherPadAsyncService.doExcuteTeacherTagAsync(dataMap, coursePackage, countDownLatch, traceKey);
        doSomething(dataMap);
        defaultRedisUtil.setString(RedisKeyConfig.COURSE_PKG_DETAIL_KEY + id, JSON.toJSONString(dataMap), RedisKeyConfig.COURSE_PKG_DETAIL_EXPIRE_TIME);
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            logger.error("异步处理线程异常", e);
        }
    }

teacherPadAsyncService.doExcuteLikeSateAsync()这个方法是异步查询点赞状态,会在dataMap里面添加一个字段state,但是在第二次请求的时候有可能发现这个字段缺失,这只是其中一个BUG。原因在于往redis里面放置信息的时机不对,大概是由于写代码太着急,正确的做法应该是在异步转同步以后再去操作redis。下面是改之后的代码:

代码语言:javascript
复制
 @Override
    public void doExecute(Map<String, Object> dataMap) {
        String cache = defaultRedisUtil.getString(RedisKeyConfig.COURSE_PKG_DETAIL_KEY + id);
        if (StringUtils.isNotBlank(cache)) {
            dataMap = JSON.parseObject(cache, Map.class);
            return;
        }
        doSomething(dataMap);
        CountDownLatch countDownLatch = new CountDownLatch(3);
        String traceKey = TraceKeyHolder.getTraceKey();
        teacherPadAsyncService.doExcuteLikeSateAsync(dataMap, coursePackage.getId(),ResourceTypeEnum.COURSE_PACKAGE.value, currentUser.getSystemId(), countDownLatch, traceKey);
        teacherPadAsyncService.doExcuteAccuracyAsync(dataMap, coursePackage.getId(), countDownLatch, traceKey);
        teacherPadAsyncService.doExcuteTeacherTagAsync(dataMap, coursePackage, countDownLatch, traceKey);
        doSomething(dataMap);
        try {
            countDownLatch.await();
                    defaultRedisUtil.setString(RedisKeyConfig.COURSE_PKG_DETAIL_KEY + id, JSON.toJSONString(dataMap), RedisKeyConfig.COURSE_PKG_DETAIL_EXPIRE_TIME);
        } catch (InterruptedException e) {
            logger.error("异步处理线程异常", e);
        }
    }

BUG的原因也比较简单,由于第一次查询的时候redis里面内容时空的,所以走了数据库查询,查询到结果后,放到redis里面,但是在存redis时候,异步的查询任务并没有完成,导致第一次请求得多的响应是对的,但是redis里面存放的却是错误的。在缓存有效期内,查询的结果都将是错误的。

当然这个实现方法的BUG不止这一个,这里不列举了,有机会再分享。


  • 郑重声明:文章首发于公众号“FunTester”,禁止第三方(腾讯云除外)转载、发表。
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-01-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 FunTester 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
云数据库 Redis
腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档