从StackOverflowError说到mybatis一级缓存

描述案发现场

  早上偶然的一次优化代码,居然写出了递归方法。下面是案发现场:
在这里插入图片描述
  讲解一下,先查询数据库,如果没有再插入数据库,再次查询然后返回。

  好像没有啥毛病,然后忽然服务器请求直线上升,请求数量达到10w次,但是线上人数很少~

排查过程

  链路监控报了StackOverflowError,数据库报的是一个插入的唯一索引冲突。StackOverflowError在官网上的解释是出现递归导致堆栈溢出。
  递归???getCode()递归?如果是插入成功的话,查询应该可以拿到鸭!

递归的假设

  接着我们就开始无头方式乱找,不过我还是坚信反推,从报错以及大量的请求来看,这b就是递归。胡适先生说过:大胆的假设,小心的求证。假设这方法是递归,说明插入之后查询到的为空,再次插入,报唯一索引错误,代码捕获了,继续执行getCode(),递归了~ bingo

验证

  通过本地的单元测试,确实递归了。石锤!接着看到为啥两次查询都为空,毕竟中间以及插入数据了!
  此时想到了Mybatis的一级缓存,如果第两次是从缓存读的,结果是一致的。单元测试也证明这一点。那为啥insert之后一级缓存不会更新呢?个人没有读mybatis缓存源码,但是推测不同mapper是属于不同sqlSession的,因为insert在其他的sevice里面,导致mapper也是不一样的,所以原本那个mapper的缓存是不会更新的,读到的还是旧的数据!

总结

  同个model的mapper最好跟同样的service处在同一个,这样的话就不会出现,新增的时候不会更新查询mapper的一级缓存。