记一次生产故障问题排查

故障现象:

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 下的缓存,这在高并发或者分布式的情况下会产生脏数据。

猜你喜欢

转载自blog.csdn.net/noob9527/article/details/115304699