在 MySQL 中,當兩個或以上的事務(wù)相互持有和請求鎖,并形成一個循環(huán)的依賴關(guān)系,就會產(chǎn)生死鎖。針對我們的業(yè)務(wù)系統(tǒng),出現(xiàn)死鎖的直接結(jié)果就是系統(tǒng)卡頓、客戶找事兒,所以我們也在想盡全力的消除掉數(shù)據(jù)庫的死鎖。
下面就和云和數(shù)據(jù)小編一起看看死鎖的三種情況及解決辦法。
死鎖的第一種情況
一個用戶A 訪問表A(鎖住了表A),然后又訪問表B;另一個用戶B 訪問表B(鎖住了表B),然后企圖訪問表A;這時用戶A由于用戶B已經(jīng)鎖住表B,它必須等待用戶B釋放表B才能繼續(xù),同樣用戶B要等用戶A釋放表A才能繼續(xù),這就死鎖就產(chǎn)生了。
解決方法:
這種死鎖比較常見,是由于程序的BUG產(chǎn)生的,除了調(diào)整的程序的邏輯沒有其它的辦法。仔細分析程序的邏輯,對于數(shù)據(jù)庫的多表操作時,盡量按照相同的順序進 行處理,盡量避免同時鎖定兩個資源,如操作A和B兩張表時,總是按先A后B的順序處理, 必須同時鎖定兩個資源時,要保證在任何時刻都應(yīng)該按照相同的順序來鎖定資源。
死鎖的第二種情況
用戶A查詢一條紀錄,然后修改該條紀錄;這時用戶B修改該條紀錄,這時用戶A的事務(wù)里鎖的性質(zhì)由查詢的共享鎖企圖上升到獨占鎖,而用戶B里的獨占鎖由于A 有共享鎖存在所以必須等A釋放掉共享鎖,而A由于B的獨占鎖而無法上升的獨占鎖也就不可能釋放共享鎖,于是出現(xiàn)了死鎖。這種死鎖比較隱蔽,但在稍大點的項 目中經(jīng)常發(fā)生。如在某項目中,頁面上的按鈕點擊后,沒有使按鈕立刻失效,使得用戶會多次快速點擊同一按鈕,這樣同一段代碼對數(shù)據(jù)庫同一條記錄進行多次操 作,很容易就出現(xiàn)這種死鎖的情況。
解決方法:
1、對于按鈕等控件,點擊后使其立刻失效,不讓用戶重復(fù)點擊,避免對同時對同一條記錄操作。
2、使用樂觀鎖進行控制。樂觀鎖大多是基于數(shù)據(jù)版本(Version)記錄機制實現(xiàn)。即為數(shù)據(jù)增加一個版本標識,在基于數(shù)據(jù)庫表的版本解決方案中,一般是 通過為數(shù)據(jù)庫表增加一個“version”字段來實現(xiàn)。讀取出數(shù)據(jù)時,將此版本號一同讀出,之后更新時,對此版本號加一。此時,將提交數(shù)據(jù)的版本數(shù)據(jù)與數(shù) 據(jù)庫表對應(yīng)記錄的當前版本信息進行比對,如果提交的數(shù)據(jù)版本號大于數(shù)據(jù)庫表當前版本號,則予以更新,否則認為是過期數(shù)據(jù)。樂觀鎖機制避免了長事務(wù)中的數(shù)據(jù) 庫加鎖開銷(用戶A和用戶B操作過程中,都沒有對數(shù)據(jù)庫數(shù)據(jù)加鎖),大大提升了大并發(fā)量下的系統(tǒng)整體性能表現(xiàn)。Hibernate 在其數(shù)據(jù)訪問引擎中內(nèi)置了樂觀鎖實現(xiàn)。需要注意的是,由于樂觀鎖機制是在我們的系統(tǒng)中實現(xiàn),來自外部系統(tǒng)的用戶更新操作不受我們系統(tǒng)的控制,因此可能會造 成臟數(shù)據(jù)被更新到數(shù)據(jù)庫中。
3、使用悲觀鎖進行控制。悲觀鎖大多數(shù)情況下依靠數(shù)據(jù)庫的鎖機制實現(xiàn),如Oracle的Select … for update語句,以保證操作最大程度的獨占性。但隨之而來的就是數(shù)據(jù)庫性能的大量開銷,特別是對長事務(wù)而言,這樣的開銷往往無法承受。如一個金融系統(tǒng), 當某個操作員讀取用戶的數(shù)據(jù),并在讀出的用戶數(shù)據(jù)的基礎(chǔ)上進行修改時(如更改用戶賬戶余額),如果采用悲觀鎖機制,也就意味著整個操作過程中(從操作員讀 出數(shù)據(jù)、開始修改直至提交修改結(jié)果的全過程,甚至還包括操作員中途去煮咖啡的時間),數(shù)據(jù)庫記錄始終處于加鎖狀態(tài),可以想見,如果面對成百上千個并發(fā),這 樣的情況將導(dǎo)致災(zāi)難性的后果。所以,采用悲觀鎖進行控制時一定要考慮清楚。
死鎖的第三種情況
如果在事務(wù)中執(zhí)行了一條不滿足條件的update語句,則執(zhí)行全表掃描,把行級鎖上升為表級鎖,多個這樣的事務(wù)執(zhí)行后,就很容易產(chǎn)生死鎖和阻塞。類似的情 況還有當表中的數(shù)據(jù)量非常龐大而索引建的過少或不合適的時候,使得經(jīng)常發(fā)生全表掃描,最終應(yīng)用系統(tǒng)會越來越慢,最終發(fā)生阻塞或死鎖。
解決方法:
SQL語句中不要使用太復(fù)雜的關(guān)聯(lián)多表的查詢;使用“執(zhí)行計劃”對SQL語句進行分析,對于有全表掃描的SQL語句,建立相應(yīng)的索引進行優(yōu)化。
總之,產(chǎn)生內(nèi)存溢出與鎖表都是由于代碼寫的不好造成的,因此提高代碼的質(zhì)量是最根本的解決辦法。