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)更新時加入主鍵,要麽就是拆分成2次操作,先查詢到對應的記錄,再根據主鍵來更新.
Mysql索引引起的死鎖