欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

记一次生产故障问题排查

程序员文章站 2024-01-27 18:17:28
...

故障现象:

org.apache.ibatis.exceptions.TooManyResultsException: Expected one result (or null) to be returned by selectOne(), but found: 2

查看数据库数据,符合条件的数据有两条。

方法上有声明式事务,同事代码具体逻辑如下:

1.条件查询xxx_deploy表,结果不为空则返回;

2.结果为空,trylock,有效时间3s;

3.加锁成功,条件查询xxx_deploy表;

4.结果不为空,解锁返回;

5.结果为空,根据条件组装对象;

6插入xxx_deploy表一条记录,解锁返回该对象;

7.加锁不成功,while循环条件查询xxx_deploy表,直到返回结果不为空,返回。

问题分析:

多线程场景下,线程A执行到步骤6,插入xxx_deploy表一条记录并解锁,线程B立刻trylock成功,条件查询xxx_deploy表时结果可能为空,这是因为方法上加了事务(对 mybatis 来说是在同一个 session 中),线程B和线程A不在同一个session ,线程B的session 中有步骤1的查询结果缓存,且未被insert更新(insert操作是在线程A的session 中发生的,只更新了线程A的session ),当执行到步骤步骤3条件查询时,由于条件相同直接走缓存,返回结果仍然为空,接着执行insert,导致数据库出现两条数据。

总结:

同一 session 且 select 调用 > 1 次。如果在两次调用中间插入 update 操作,缓存会立即失效。只要 session 中有 insert、update 和 delete 语句,该 session 中的缓存会立即被刷新。但是注意这只是在同一 session 之间。不同 session 之间如 session1 和 session2,session1 里的 insert/update/delete 并不会影响 session 2 下的缓存,这在高并发或者分布式的情况下会产生脏数据。