鑽石舞台 發表在 痞客邦 留言(0) 人氣()



鑽石舞台 發表在 痞客邦 留言(0) 人氣()



鑽石舞台 發表在 痞客邦 留言(0) 人氣()

再見世界(Goodbye World)

鑽石舞台 發表在 痞客邦 留言(0) 人氣()

傭兵重生:回憶山貓(Mercenaries Rebirth: Call of the Wild Lynx)

Chess Variants - Omnichess

鑽石舞台 發表在 痞客邦 留言(0) 人氣()

《諾娃獨立遊戲通訊》系列主要介紹一些近期由站內開發者創建以及值得開發者關注的內容,內容基本上來自 indienova 站內。

鑽石舞台 發表在 痞客邦 留言(0) 人氣()

芝加哥美食節剛剛結束

鑽石舞台 發表在 痞客邦 留言(0) 人氣()

要問在芝加哥

鑽石舞台 發表在 痞客邦 留言(0) 人氣()

獨立日×小長假

飯糰外賣

鑽石舞台 發表在 痞客邦 留言(0) 人氣()

(給ImportNew加星標,提高Java技能)

問題背景

我們在開啟多線程對數據庫進行操作的時候,先批量對數據進行刪除,然後再新增,本來想着是考慮到不走更新,性能會提升,但是執行的時候發現報錯,執行的sql等待超時,阻塞了進程,dbcp連接池被打滿,數據庫表訪問不可用。針對這個問題,我們進行了深入的挖掘,逐漸解開了問題的真相。
問題復現復現準備工作
業務問題這裡就不細講了,為了方便講解,操作直接簡化。
數據庫表a有唯⼀索引t1,業務字段t2,主鍵id。數據庫表a的定義如下:
現在導⼊⼀批數據A的集合,A的定義如下所示:
接下來復現問題操作
根據t1的值查詢表a中有沒有對應的記錄
如果有值,則更新t2的值
如果沒查到結果,則執行insert插入操作
這裡批量操作我們採用了多線程的方式來執行
不走更新操作,先刪除,後插入,保證只有2次數據庫操作。
從業務的⻆度考慮,表中只有id,t1,t2字段,且客戶端實際也只關⼼t2字段。因此決定先根據A的t1字段去數據庫⾥將對應的值刪全部除,然後進⾏批量插⼊操作,這樣的話,⽆論更新還是插⼊,都只有兩次數據庫操作,提高了性能。為了避免插⼊失敗,導致數據被刪除,在handle⽅法上添加事務。
復現報錯
報錯如下:
Deadlock found when trying to get lock; try restarting transaction; nested exception is
com.ibatis.common.jdbc.exception.NestedSQLException...
可以看到,發⽣了數據庫死鎖。於是開始排查問題
問題原因
查詢相關資料得知,引起死鎖的原因是MYSQL的間隙鎖。
間隙鎖
間隙鎖(Gap Lock)是Innodb在可重複讀提交下為了解決幻讀問題時引⼊的鎖機制,幻讀的問題存在是因為新增或者更新操作,這時如果進⾏範圍查詢的時候(加鎖查詢),會出現不⼀致的問題,這時使⽤不同的⾏鎖已經沒有辦法滿⾜要求,需要對⼀定範圍內的數據進⾏加鎖,間隙鎖就是解決這類問題的。在可重複讀隔離級別下,數據庫是通過⾏鎖和間隙鎖共同組成的(next-key lock)來實現的。
⾏鎖和間隙鎖的定義如下所示:
record lock:⾏鎖,也就是僅僅鎖着單獨的⼀⾏。
gap lock:間隙鎖,僅僅鎖住⼀個區間(注意這⾥的區間都是開區間,也就是不包括邊界值)。
next-key lock:record lock+gap lock,所以next-key lock也就半開半閉區間,且是下界開,上界閉。
加鎖規則特性
加鎖規則有⼀些特性,其中我們需要關注的有:
加鎖的基本單位是(next-key lock),他是前開後閉原則
查找過程中訪問的對象會增加鎖
間隙鎖僅阻⽌其他事務插⼊間隙。在刪除數據的時候,會去加間隙鎖,但是多個事務是可以同時對⼀個間隙去加鎖的,⽽如果需要對該間隙進⾏插⼊,則需要等待鎖釋放。
間隙鎖復現死鎖
下⾯可以做個實驗,去復現死鎖問題。
step1- ⾸先插⼊測試數據
表a數據初始化後,這時候那麼可能的next-key lock的包括:
(⽆窮⼩, 10],(10,11],(11,13],(13,20],(20, ⽆窮⼤)
step2- 我們開啟兩個窗⼝去模擬死鎖。
Session1:
Session2:
此時,Session 1和Session 2都會對區間(20, ⽆窮⼤)加鎖, 因為間隙鎖只是⽤來防⽌其他事務在區間中插⼊數據。
step3- Session1繼續插⼊操作:
此時Session1阻塞(因為Session2持有間隙鎖)。
step4- 緊接着Session2繼續插⼊操作:
此時Session2死鎖,因為Session1持有間隙鎖。⽽我們的代碼⾥⾯,因為涉及到多線程在事務⾥進⾏先刪除後插⼊的操作,就會發⽣死鎖。
解決方式1、將事務隔離級別將為read commit.
間隙鎖只存在於可重複讀的隔離級別下,因為要防⽌幻讀。這個⽅法不現實,不可能為了這個問題 把整個線上數據庫隔離級別給改掉。
2、避免先刪除後插⼊的操作.
修改代碼,避免先刪除後插⼊的操作。犧牲性能,在業務中,先根據唯⼀索引查出存在的記錄,然後對存在的記錄進⾏根據主鍵Id在循環中更新,對於不存在的記錄進⾏批量插⼊。

鑽石舞台 發表在 痞客邦 留言(0) 人氣()