记一次生产故障问题排查
程序员文章站
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 下的缓存,这在高并发或者分布式的情况下会产生脏数据。