多次查询返回结果与数据库不一致,并且多次查询的结果也不一样(java、数据库事务隔离级别、mybatis缓存、事物)
首先,问题的现象为在系统运行过程中,未对数据库表进行更新或插入时,同一个查询sql语句,查询得到的结果是不一样的。数据库中的数据如下图t_user表idname1张三2李四3王五如果这个接口的查询是这么一条查询语句一条语句 select * from where id>0;正常的输出应该是idname1张三...
首先,问题的现象为在系统运行过程中,未对数据库表进行更新或插入时,同一个查询sql语句,多次查询返回结果与数据库不一致,并且多次查询的结果也不一样。
数据库中的数据如下图
t_user表
id | name |
1 | 张三 |
2 | 李四 |
3 | 王五 |
如果这个接口的查询是这么一条查询语句一条语句 select * from where id>0;
正常的输出应该是
id | name |
1 | 张三 |
2 | 李四 |
3 | 王五 |
但是这条语句在程序中执行的结果可能是
id | name |
1 | 张三 |
2 | 李四 |
或者
id | name |
1 | 张三 |
2 | 李四 |
3 | 王五 |
4 | 赵六 |
以及其他曾经这张表的某个时段的状态(偶发现象);并且伴随着其他提交写库的sql事物跑的很慢。
根据这个现象分析问题,我们首先会把他分为两个问题来考虑,首先多次查询查到数据不一致的问题,想到的是可能是mybatis的一级、二级缓存问题。从这个思路出发,把mybatis的二级缓存关闭,把一级缓存的粒度设置为statement级别。
重启服务之后发现查询数据能够查询出正常的数据了;但是在系统运行一段时间后,这个问题又出现了。
之后把mybatis的打印sql日志的配置打开
mybatis-plus.configuration.log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
在系统运行时查询日志发现一些没有数据库事物的接口在执行时,这个sql会加入到某个已存在的事物中。
根据这个现象作为突破口,定位这个事物id创建时,是哪条sql定位到该代码段,对代码段进行分析。最后发现该代码段是一个定时任务,并且存在有手动编程式(即注入DefaultTransactionDefinition)然后手动起一个事物,但是在try--catch中有个分支没有对该事物进行提交或者回滚,导致了这个事物一直挂起在那里,其他线程进来时就有可能加入到了这个事物中,因为数据库的事物隔离级别是可重复读,所以单个事物内读取都是快照读,所以就会造成某些加入了这个事物的线程读出来的结果是之前数据库某个时刻的数据状态。
如果某个写库的操作也加入了这个事物,这个事物在没提交,也不回滚的状态下导致业务卡死,就是出现了锁表的现象,其实是因为事物没有提交。
解释:一个线程没有正常提交事务,那么事务就会被挂起,当线程销毁后,数据库连接归还给连接池,对应的数据库连接的状态就还存在事务未提交。
其他的线程进来复用链接资源的时候,就会不知不觉被加入到已存在的事务中,甚至没有事务的查询都会加入到已存在的事务中,因为mybatisSqlSession他会先看看当前链接有没有事务,有就fetch旧的出来,没有就生成一个新的session。所以有概率出现,查询时会拿到未完成事务提交的session。
更多推荐
所有评论(0)