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

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

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

 @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。下面是改之后的代码:

 @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”,禁止第三方(腾讯云除外)转载、发表。

技术类文章精选

非技术文章精选

最新文章

  1. mysql 主从复制实现步骤
  2. struts之动态方法调用改变表单action属性
  3. 异步SRAM控制器的Verilog建模
  4. jQuery知识大杂汇
  5. Unity3D设置字体颜色大小,用于游戏分数显示设置等,
  6. 使用C#开发ActiveX控件 11
  7. .net 程序集
  8. numpy 实践记录
  9. 剑指offer面试题6 重建二叉树(c)
  10. Python开发【框架篇】Django的Form组件
  11. FTP文件上传并支持断点续传(一)—— win10 本地环境 ftp站点构建
  12. CMS Collector and G1 Collector
  13. 使用Python进行OCR -- 识别图片中的文字
  14. Redis入门到高可用(十二)—— pipeline
  15. 校园网突围之路由器开wifi__windows版
  16. TCP和UDP的9个区别是什么
  17. 由浅入深了解Retrofit(一)
  18. 加油吧 骚年QAQ
  19. landa语法
  20. 「日常训练」 不容易系列之(3)—— LELE的RPG难题 (HDU 2045)

热门文章

  1. C# AutoResetEvent 理解
  2. linux PCI 寻址
  3. es6笔记 day2---字符串模板及字符串新增
  4. 【Ubuntu】16.04网卡信息配置
  5. Perl 的继承
  6. DEVOPS技术实践_22:根据参数传入条件控制执行不同stage
  7. asp.net保留两位小数
  8. Django2.2 Cache缓存的设计以及几种方式的 多级或单级缓存处理
  9. 使用Selenium对网页元素进行定位的诸种方法
  10. 「CH2401」送礼物 解题报告