Mybatis一級緩存的坑
前序
接着上篇文章插入唯一數據的各種問題。中間過程說Spring的事務,是不是在生命週期的時候程序提交了事務,但是數據庫沒有提交事務。其實不會的,當時寫文章的時候只是根據當時的現象去推測,後來我用代碼測試實際是會提交事務的。Spring是沒有錯的。代碼如下:
@Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.READ_COMMITTED)
public void update(User user) {
// 根據username查詢用戶,username普通索引
// SQL: select * from user where username = #{username} limit 1
User top = userMapper.findTopByUsername(user);
if (null == top) {
System.out.println("lock 之前 = " + Thread.currentThread().getName());
lock.lock();
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
@SneakyThrows
@Override
public void afterCompletion(int status) {
System.out.println("解鎖 = " + Thread.currentThread().getName());
Thread.sleep(30000);
lock.unlock();
super.afterCompletion(status);
}
});
// SQL: select * from user where username = #{username} limit 1
top = userMapper.findTopByUsername(user);
System.out.println("second top = " + top);
if (null == top) {
top = new User();
top.setUsername(user.getUsername())
.setNum(user.getNum())
;
userMapper.save(top);
return;
}
}
userMapper.update(user);
}
之前說過,併發進入第一個null判斷的線程會串行化執行lock到unlock的代碼,但是表現的是,併發的線程在第二次查詢裏就會讀不到值,整段表現的是不可重複讀的現象。但實際隔離級別是讀提交,不符合原理。於是我就猜測Spring事務提交和真實事務提交可能有差別,還在Spring源碼中找到了一段註釋。後來想想還是親手驗證一下,以防誤人子弟,那就不好了,所以就改成上面代碼。如果我的猜測正確,那在unlock執行之前,數據庫就查不到新插入的這條記錄。實際上在該線程sleep期間,我去數據庫是可以查詢到最新插入的記錄,這說明我的猜測是錯誤,對那段源碼註釋也理解錯了。
到此,問題又沒有思緒了,突然有一天,朋友在討論Mybatis緩存。此時豁然開朗了,在Spring事務中,是會使用Mybatis的一級緩存的。瞬間可以解釋所有的現象了。爲什麼呢?因爲併發進入第一個空判斷的線程,之前執行過一次查詢,所以第二次查詢不會去查詢數據庫,而是會直接緩存讀。因此就像可重複讀一樣,在此出現了。那爲什麼for update查詢又是可以查到的呢?因爲for update查詢Mybatis不會走一級緩存,所以for update查詢是可以讀到上一個線程插入的值。後來看Mybatis的查詢日誌,發現和這個設想一模一樣。
緩存坑總結
後來百度了一下,發現在多線程環境下mybatis的緩存會很多坑,各種莫名其妙的問題出現,因爲它的session線程不安全,看源碼就知道,各種字段變量。
- 破壞事務的語義,例子在上面;
- 髒數據;
【參考】
mybatis一級緩存讓我憔悴 https://www.cnblogs.com/Yatces/p/12342481.html
MyBatis 一級緩存在分佈式下的坑 https://blog.csdn.net/valada/article/details/104012588
MyBatis一級緩存引起的無窮遞歸 https://www.cnblogs.com/Leo_wl/p/5377121.html
mybatis的一級緩存會不會產生髒數據問題? https://www.zhihu.com/question/53321129