有一天在查詢生產(chǎn)異常日志的時候,突然發(fā)現(xiàn)了Mysql死鎖的日志,第一反應(yīng)則是:臥槽,我寫的代碼居然有死鎖,帶著疑問開始了長達(dá)半個小時的百度之旅,最終才知道原因是索引合并導(dǎo)致。
何為索引合并
1、顧名思義,索引合并,就是把多個索引到的數(shù)據(jù)合并成一個
2、索引合并的時候,會對索引進(jìn)行并集,交集操作,以便合成一個索引。
3、這些需要合并的索引只能是一個表的,不能對多表進(jìn)行索引合并。
舉個例子
在下面的sql中,字段name和age都分別建了索引,查詢時會分別通過name=lyy和age=18查找符合條件的數(shù)據(jù),然后進(jìn)行合并獲取到主鍵id,再回表查詢,然后對兩個條件查詢到的id進(jìn)行交集,這個交集合并的過程就叫索引合并。
select * from user where name = 'lyy' and age = 18

為什么索引合并會導(dǎo)致死鎖
死鎖:我們先回顧下,什么是死鎖?
兩個或者兩個以上的進(jìn)程在執(zhí)行的過程中,因爭奪一些公共的資源,導(dǎo)致雙方都占有了對方需要獲取到才能繼續(xù)往下執(zhí)行的不同資源時,發(fā)生了互相一直等待的情況。這就叫死鎖。
因?yàn)樗饕喜l(fā)生死鎖的sql
update sop_task_statistics set task_num = task_num + #{dto.taskNum}, score = score + #{dto.score}
where slxt_account = #{dto.slxtAccount} and area_id = #{dto.areaId} and month_date = #{dto.monthDate}
因?yàn)閿?shù)據(jù)庫表分別對slxt_account、area_id、month_date字段建了索引,所以在執(zhí)行上面的sql時會對上面的三個字段索引進(jìn)行了鎖定操作,并且進(jìn)行了回表操作時也對主鍵索引進(jìn)行了鎖定,所以對索引的鎖定次數(shù)達(dá)到了6次。
為什么這個sql會導(dǎo)致死鎖?
我們用兩個事務(wù)來分析下該sql的執(zhí)行過程

如何解決上面的死鎖
1、創(chuàng)建組合索引(slxt_account,area_id,month_date),這樣在執(zhí)行上面的sql語句時,通過組合索引直接定位到具體的主鍵索引id,更新操作變成了行鎖,并且鎖粒度細(xì)化到一行,這樣就不會導(dǎo)致兩個事務(wù)都鎖定了對方需要獲取到才能繼續(xù)往下執(zhí)行的資源。
2、先查詢,查詢操作不會發(fā)生鎖操作,查詢到具體的主鍵id,然后通過id進(jìn)行更新操作,也就是更新sql改為下面這樣
update sop_task_statistics set task_num = task_num + #{dto.taskNum}, score = score + #{dto.score} where id = #{id}