likes
comments
collection
share

不懂mybatis缓存引发的问题

作者站长头像
站长
· 阅读数 21

前言

大家好,我是大飞! 一个mybatis缓存引发的问题,消耗一桶泡面,耗时3小时才解决。本篇文章主要是分享解决问题、分析问题的过程,如果大家平时很少通过debug源码的方式去解决问题,不妨大家看看我下面的分析问题思路,以及debug源码的流程。我相信还是可以帮到大家的。

问题描述

今天下午快下班了,产品说线上有bug,然后同事小王去排查去了,说好奇怪呀,为啥直接去数据库查的时候没有数据,但是程序打断点确有数据。然后同事们纷纷围了上去,大家都发现好奇怪呀,看见小王束手无策的样子,我的另一个同事王麻子就说让我来看看。王麻子看了一会,摇摇头说,奇怪执行了这个mapper接口居然没打sql,其他的mapper 都打了sql日志的呀。

问题eg:List ret = mapper.select(Obejct parameter); sql 在数据库中执行没有值的。但是程序输出的结果ret的长度确为1。

大家都没找到原因,于是我大飞也加入了战斗,下面我们就开始分析,排查吧!


排查思路:

小王说程序给的结果和把sql直接去数据库查的不一样,王麻子说,这个mapper接口没有打印日志

1. 会不会不是执行的这个mapper,难道是有两个同样的mapper接口。

为什么我会这么想呢,因为我们这个工程里面包含两个项目的包,启动其中一个项目,两个项目的代码都会加载进去。我就在想会不会是注入混了。全局搜索了一下没有发现同名的mapper也没有同名的service。

2.只有打断点进入mybatis源码,看看sql是否和mapper写的一样

进入mapper代理执行的内部,一步一步往下走,中间无数步就忽略,最终到了excutor.query了(org.apache.ibatis.session.defaults.DefaultSqlSession selectList方法),看ms里面的sqlSource,以及parameter都和预期的一样。于是到了这儿我还是没发现问题

不懂mybatis缓存引发的问题

3.进入深入debug,发现关键问题。

进入到上一步,还是没有发现sql有啥问题,我就继续深入的debug,直到我debug到org.apache.ibatis.executor query方法152方法时,发现localCache我就明白离真相不远了。

不懂mybatis缓存引发的问题

可以看到这个地方已经出现了localCache,确实数据的值是从localCache取出来的,但是从cache里面取出来的值就有问题了,而且Cache 缓存的key信息里面包含了sql和入参,sql和入参和预期的值是一样的,就是value不是预期值。这时候我就想既然缓存里面有值,那么在某一步肯定是提前put了的,所以我就在put的方法打了断点。

不懂mybatis缓存引发的问题

因为这个执行sql的地方肯定很多,所以我这个地方的短单就打了一个条件断点key.toString().equals("-1616...."),重新调试进入了我预期的put 方法,我以此时的value是有值的,但是并非我所愿,这个地方的value是没有值的,也就是和sql执行的结果一样,无数据。

到了这一步,我可以肯定的是sql和参数确实是没有被修改,应该是缓存结果被修改了,或者说是中途又put了一次把数据放进去了。无赖只有继续往下走,看看是不是中途,有什么拦截器,把值给我修改了

继续往下走,又到了执行我那个有问题的sql了,发现到了我这儿缓存里面的值就变成了List.size == 1的情况了。

看到这儿不知道他家有啥想法没? 也就是说我这个sql 在我这个业务逻辑中,确实有两个地方执行了,第一次执行返回的list.size == 0,等于0也是我们期望的值,并且第一次执行了就放到缓存了。第二次执行同样的sql,就直接从缓存中取,按理说取出来的也是一个空集合,但是取出来却有值了。。。。

4.找到问题代码

到这儿大概原因我是明白了,放进缓存中的值是没有问题,并且后续也没有再重新put值进来(通过debug put方法得出),剩下的可能就是这个value值被修改了。回到第一个执行mapper接口的地方,我终于发现问题了。 伪代码:

List ret = mapper.select(Obejct parameter); 
ret.add(1234l)

TMD,第一次查询sql的时候,在结果集上面加了一个值。。。。导致后面查询来就是这个值。关键是某些场景下面,第二次查询出来的值本身也等于这个add的值,导致测试反应预生产环境没有这个问题。


总结:

看到这儿,原来是开发直接在原有结果集的引用上修改了值,导致后面查询出来的是被修改过的值。第二次查询缓存中的数据,也没有打印sql,让同事们连连叫怪。。。。。最后大家还是不要直接修改mapper结果集的数据。。。。。。。。。看来我们的八股还是没有背好呀。

所以大家遇到奇怪的问题不要怕跟源码,直接干就完了,当然更源码的时候一定要学会用条件断点。不然会把自己跟蒙的。

转载自:https://juejin.cn/post/7383311950174650406
评论
请登录