您好,登錄后才能下訂單哦!
這篇文章將為大家詳細(xì)講解有關(guān)MySQL 數(shù)據(jù)庫中update語句會不會發(fā)生死鎖,文章內(nèi)容質(zhì)量較高,因此小編分享給大家做個參考,希望大家閱讀完這篇文章后對相關(guān)知識有一定的了解。
最近生產(chǎn)上的 MySQL 數(shù)據(jù)庫,是不是的就來一次 DeadLock,其中我做了故障排查,昨天做了相關(guān)的升級,導(dǎo)致昨天非常的忙,很多網(wǎng)友加我好友,都沒有及時回應(yīng),直到晚上升級結(jié)束,我在群里做了相關(guān)的解釋!
截了一段錯誤日志信息如下:
其中涉及到的更新語句如下:
很奇妙吧,執(zhí)行一條 update sql 竟然會有死鎖?
具體死鎖的提前是 i_pay_record 表中的 order_id 字段有索引。
下面我們通過新建一張表 xttblog,來說明問題。
當(dāng)我們執(zhí)行下面的 update 語句時,就有可能發(fā)生死鎖!
mysql 的事務(wù)支持與存儲引擎有關(guān),MyISAM 不支持事務(wù),INNODB 支持事務(wù),更新時可能采用的是行級鎖,也可能是表級鎖。我們這里采用的是 INNODB 做存儲引擎,意味著會將 update 語句做為一個事務(wù)來處理。前面的文章中我提到了行級鎖必須建立在索引的基礎(chǔ)上,上面的更新語句用到了索引 idx_1,所以這里肯定會加上行級鎖。
行級鎖并不是直接鎖記錄,而是鎖索引(前面的文章也解釋過)。
如果一條 SQL 語句用到了主鍵索引,mysql 會鎖住主鍵索引;如果一條語句操作了非主鍵索引,mysql 會先鎖住非主鍵索引,再鎖定主鍵索引。
這個 update 語句會執(zhí)行以下步驟:
由于用到了非主鍵索引,首先需要獲取 idx_1 上的行級鎖
緊接著根據(jù)主鍵進行更新,所以需要獲取主鍵上的行級鎖
更新完畢后,提交,并釋放所有鎖
如果在步驟 1 和 2 之間突然插入一條語句:update xttblog …..where id=? and user_id=? 這條語句,那么會先鎖住主鍵索引,然后鎖住 idx_1。
這時,悲劇就發(fā)生了!
一條語句獲取了 idx_1 上的鎖,等待主鍵索引上的鎖;另一條語句獲取了主鍵上的鎖,等待 idx_1 上的鎖,這樣就出現(xiàn)了死鎖。
很驚奇吧,其實一點也不奇怪,只要你了解了 MySQL 的一些底層設(shè)計原理!
那么發(fā)生這種問題,有解決方案嗎?
當(dāng)然有了,要不然我寫這篇文章干什么?
解決方案,最笨最靠譜的做法就是:先獲取需要更新的記錄的主鍵,然后再逐條更新!
這樣就可以解決問題了,但是這個解決方案與先前的更新語句不一樣,先前的更新語句對所有記錄的更新在一個事務(wù)中,采用循環(huán)更新后并不在同一個事務(wù)中,所以在 for 循環(huán)外面還得開一個事務(wù)。
在采用 INNODB 的 MySQL 中,更新操作默認(rèn)會加行級鎖,行級鎖是基于索引的,在分析死鎖之前需要查詢一下 mysql 的執(zhí)行計劃,看看是否用到了索引,用到了哪個索引,對于沒有用索引的操作會采用表級鎖。如果操作用到了主鍵索引會先在主鍵索引上加鎖,然后在其他索引上加鎖,否則加鎖順序相反。在并發(fā)度高的應(yīng)用中,批量更新一定要帶上記錄的主鍵,優(yōu)先獲取主鍵上的鎖,這樣可以減少死鎖的發(fā)生。
不是說 update 不會發(fā)生死鎖,而是你的程序沒遇到高并發(fā)而已!
關(guān)于MySQL 數(shù)據(jù)庫中update語句會不會發(fā)生死鎖就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,可以學(xué)到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。