Mysql索引引起的死鎖


提到索引,首先想到的是效率提高,查詢速度提升,不知不覺都會有一種心理趨向,管它三七二十一,先上個索引提高一下效率..但是索引其實也是暗藏殺機的...

今天壓測帶優化項目,開着Jmeter高並發訪問項目,后台連着mysql通過show processlist命令查看查詢情況,發現些sql語句需要優化,就在關鍵字段上上了索引.效果很明顯項目的吞吐量瞬間提高到原來3倍,但是問題也出現了,日志中報出大量的死鎖錯誤.本來以為程序邏輯有問題,定位了一下程序塊,大體邏輯如下:

userDao.updateByUserId(userId);
userDao.save(user);

簡單說就是,一個按照表中的userId字段進行更新,一個按照表中的主鍵進行保存.邏輯上看不出來什么問題,前前后后也嘗試了各種方法,並添加了各種日志進行排查都無果.一籌莫展之際想到這個問題是在我添加索引之后出現的,也許和索引有關,之前記得讀到過有關索引引起問題的帖子,但是具體內容已經忘記了,然后去掉了表中的索引(userId字段)后,果然再也不報錯了,死鎖消失了.進而探究了一下原因:

找到一篇博客進行了比較詳細的介紹: http://blog.csdn.net/aesop_wubo/article/details/8286215

簡要的說就是Mysql的innodb引擎支持事務,更新時采用的是行級鎖,會在使用中的索引上加鎖,如果使用的主鍵索引,直接鎖主鍵索引,如果使用的非主鍵索引,則先鎖索引,再鎖對應的主鍵索引.故而在根據非主鍵索引進行更新時,實際上需要3步:

1)先獲取索引鎖

2)獲取對應記錄的主鍵鎖

3)按照主鍵完成更新操作

在高並發的情況下實際上這里就存在問題了, 由於上面說的1)和2)是按照先鎖索引,再鎖主鍵的順序,那么只要存在先鎖主鍵,再鎖索引這種反順序操作那么就能達成死鎖.也就是說一個獲取了索引鎖,等待主鍵鎖,另一個獲取了主鍵鎖,等待獲取索引鎖.造成互相等待的死鎖情況.

這里補充說明一下我上面例子中為何導致死鎖:
userDao.updateByUserId(userId)這句是按照非主鍵索引更新,故先鎖非主鍵索引,再鎖主鍵索引.
userDao.save(user)這句按照主鍵保存對象,由於更新的字段中包含索引字段,故在獲取主鍵索引后,需要獲取索引字段的鎖,以便完成字段更新.

這就滿足了獲取鎖的順序與上一句完全相反,達成死鎖條件~

 

解法:

說了這么多,解決方案也就明晰了,只要讓更新操作中帶有主鍵即可.也就是讓獲取鎖的順序一致即可.我這里的例子userDao.updateByUserId(userId)更新時加入主鍵,要么就是拆分成2次操作,先查詢到對應的記錄,再根據主鍵來更新.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM