前言:
最近生产环境的余额系统在扣减余额时经常出现余额够但是提示余额不足无法扣减的情况。查看代码逻辑,发现会先查询一次判断余额是否够,再实际扣减,之后查看日志发现并没有执行查询语句就返回错误了,推测可能是 MyBatis 一级缓存没有关闭引起的脏数据问题。关闭一级缓存后果然恢复正常了。
所以有了这篇文章,验证下 MyBatis 一级缓存的生效条件。
一级缓存
MyBatis 默认会开启一级缓存,在同一次会话中,如果执行多次查询条件相同的 SQL,会进行优化,优先命中一级缓存,避免多次查询数据库。
这样在单机环境下是没有问题的,可以减少于数据库的交互。但是生成环境一般都是多机部署,这样一级缓存开启的情况下就容易出现脏数据。
接下来做两个实验验证下。
测试 1
public void test1() {
SqlSession sqlSession = sqlSessionFactory.openSession(true);
UserWalletMapperExt mapper = sqlSession.getMapper(UserWalletMapperExt.class);
System.out.println(mapper.queryUserBalance(10L, 1));
System.out.println(mapper.queryUserBalance(10L, 1));
System.out.println(mapper.queryUserBalance(10L, 1));
mapper.addBalanceByType(10L, 1, 100L);
System.out.println(mapper.queryUserBalance(10L, 1));
System.out.println(mapper.queryUserBalance(10L, 1));
}
...queryUserBalance:debug:181 ==> Preparing: select balance from user_wallet WHERE user_id = ? and type = ?
...queryUserBalance:debug:181 ==> Parameters: 10(Long), 1(Integer)
...queryUserBalance:debug:181 <== Total: 1
200
200
200
...addBalanceByType:debug:181 ==> Preparing: update user_wallet set balance = balance + ? where user_id = ? and type = ?
...addBalanceByType:debug:181 ==> Parameters: 100(Long), 10(Long), 1(Integer)
...addBalanceByType:debug:181 <== Updates: 1
...queryUserBalance:debug:181 ==> Preparing: select balance from user_wallet WHERE user_id = ? and type = ?