SQL Server數(shù)據(jù)庫(kù)的死鎖詳細(xì)說(shuō)明
一、了解死鎖
死鎖是導(dǎo)致數(shù)據(jù)庫(kù)中的競(jìng)爭(zhēng)性并發(fā)鎖,通常在多步驟事務(wù)中。
當(dāng)兩個(gè)或多個(gè)任務(wù)永久相互阻止時(shí),每個(gè)任務(wù)都鎖定了其他任務(wù)嘗試鎖定的資源,就會(huì)發(fā)生死鎖。例如:
- 事務(wù) A 獲取第 1 行上的共享鎖。
- 事務(wù) B 獲取第 2 行上的共享鎖。
- 事務(wù) A 現(xiàn)在請(qǐng)求第 2 行上的獨(dú)占鎖,并被阻止,直到事務(wù) B 完成并釋放第 2 行上的共享鎖。
- 事務(wù) B 現(xiàn)在請(qǐng)求第 1 行上的獨(dú)占鎖,并被阻止,直到事務(wù) A 完成并釋放它在第 1 行上的共享鎖。
事務(wù) A 在事務(wù) B 完成之前無(wú)法完成,但事務(wù) B 被事務(wù) A 阻止。此條件也稱為循環(huán)依賴關(guān)系:事務(wù) A 依賴于事務(wù) B,事務(wù) B 通過(guò)對(duì)事務(wù) A 的依賴來(lái)關(guān)閉循環(huán)。
死鎖中的兩個(gè)事務(wù)將永遠(yuǎn)等待,除非死鎖被外部進(jìn)程打破。SQL Server 數(shù)據(jù)庫(kù)引擎死鎖監(jiān)視器定期檢查處于死鎖狀態(tài)的任務(wù)。如果監(jiān)視器檢測(cè)到循環(huán)依賴關(guān)系,它將選擇其中一個(gè)任務(wù)作為受害者,并在出現(xiàn)錯(cuò)誤的情況下終止其事務(wù)。這允許其他任務(wù)完成其事務(wù)。具有因錯(cuò)誤而終止的事務(wù)的應(yīng)用程序可以重試該事務(wù),該事務(wù)通常在另一個(gè)死鎖事務(wù)完成后完成。
死鎖經(jīng)常與正常阻塞混淆。當(dāng)一個(gè)事務(wù)請(qǐng)求鎖定另一個(gè)事務(wù)鎖定的資源時(shí),請(qǐng)求事務(wù)將等待,直到鎖定被釋放。默認(rèn)情況下,除非設(shè)置了LOCK_TIMEOUT,否則 SQL Server 事務(wù)不會(huì)超時(shí)。請(qǐng)求事務(wù)被阻止,而不是死鎖,因?yàn)檎?qǐng)求事務(wù)沒(méi)有執(zhí)行任何操作來(lái)阻止擁有鎖的事務(wù)。最終,擁有事務(wù)將完成并釋放鎖,然后請(qǐng)求事務(wù)將被授予鎖定并繼續(xù)。死鎖幾乎可以立即解決,而阻塞理論上可以無(wú)限期地持續(xù)存在。僵局有時(shí)被稱為致命的擁抱。
死鎖是可能發(fā)生在具有多個(gè)線程的任何系統(tǒng)上的情況,而不僅僅是在關(guān)系數(shù)據(jù)庫(kù)管理系統(tǒng)上,并且可能發(fā)生在數(shù)據(jù)庫(kù)對(duì)象上的鎖以外的資源上。例如,多線程操作系統(tǒng)中的線程可能會(huì)獲取一個(gè)或多個(gè)資源,例如內(nèi)存塊。如果要獲取的資源當(dāng)前由另一個(gè)線程擁有,則第一個(gè)線程可能必須等待擁有該線程釋放目標(biāo)資源。據(jù)說(shuō)等待線程依賴于該特定資源的擁有線程。在 SQL Server 數(shù)據(jù)庫(kù)引擎實(shí)例中,會(huì)話在獲取非數(shù)據(jù)庫(kù)資源(如內(nèi)存或線程)時(shí)可能會(huì)死鎖。
在圖中,事務(wù) T1 依賴于表鎖資源的事務(wù) T2。同樣,事務(wù) T2 依賴于表鎖資源的事務(wù) T1。由于這些依賴項(xiàng)形成一個(gè)循環(huán),因此事務(wù) T1 和 T2 之間存在死鎖。
當(dāng)對(duì)表進(jìn)行分區(qū)并將 的設(shè)置設(shè)置為 AUTO 時(shí),也會(huì)發(fā)生死鎖。設(shè)置為 AUTO 時(shí),通過(guò)允許 SQL Server 數(shù)據(jù)庫(kù)引擎在 HoBT 級(jí)別而不是表級(jí)別鎖定表分區(qū),并發(fā)性會(huì)增加。但是,當(dāng)單獨(dú)的事務(wù)在表中持有分區(qū)鎖并希望在其他事務(wù)分區(qū)上的某個(gè)位置使用鎖時(shí),這會(huì)導(dǎo)致死鎖。這種類型的死鎖可以通過(guò)設(shè)置為 ;盡管此設(shè)置將通過(guò)強(qiáng)制對(duì)分區(qū)進(jìn)行大量更新以等待表鎖定來(lái)降低并發(fā)性。
二、檢測(cè)并結(jié)束死鎖
當(dāng)兩個(gè)或多個(gè)任務(wù)永久相互阻止時(shí),每個(gè)任務(wù)都鎖定了其他任務(wù)嘗試鎖定的資源,就會(huì)發(fā)生死鎖。下圖顯示了死鎖狀態(tài)的高級(jí)視圖,其中:
- 任務(wù) T1 鎖定了資源 R1(由從 R1 到 T1 的箭頭指示),并請(qǐng)求鎖定資源 R2(由從 T1 到 R2 的箭頭指示)。
- 任務(wù) T2 鎖定了資源 R2(由從 R2 到 T2 的箭頭指示),并請(qǐng)求鎖定資源 R1(由從 T2 到 R1 的箭頭指示)。
- 由于在資源可用之前,這兩個(gè)任務(wù)都無(wú)法繼續(xù),并且在任務(wù)繼續(xù)之前無(wú)法釋放這兩個(gè)資源,因此存在死鎖狀態(tài)。
SQL Server 數(shù)據(jù)庫(kù)引擎會(huì)自動(dòng)檢測(cè) SQL Server 中的死鎖周期。SQL Server 數(shù)據(jù)庫(kù)引擎選擇其中一個(gè)會(huì)話作為死鎖受害者,當(dāng)前事務(wù)將終止,并顯示錯(cuò)誤以打破死鎖。
2.1、可能死鎖的資源
每個(gè)用戶會(huì)話可能代表其運(yùn)行一個(gè)或多個(gè)任務(wù),其中每個(gè)任務(wù)可能獲取或等待獲取各種資源。以下類型的資源可能會(huì)導(dǎo)致阻塞,從而導(dǎo)致死鎖。
鎖。等待獲取資源(如對(duì)象、頁(yè)面、行、元數(shù)據(jù)和應(yīng)用程序)的鎖可能會(huì)導(dǎo)致死鎖。例如,事務(wù) T1 在行 r1 上具有共享 (S) 鎖,并且正在等待在 r2 上獲取獨(dú)占 (X) 鎖。事務(wù) T2 在 r2 上具有共享 (S) 鎖,并且正在等待在行 r1 上獲取獨(dú)占 (X) 鎖。這會(huì)導(dǎo)致鎖定循環(huán),其中 T1 和 T2 相互等待對(duì)方釋放鎖定的資源。
工作線程。等待可用工作線程的排隊(duì)任務(wù)可能會(huì)導(dǎo)致死鎖。如果排隊(duì)的任務(wù)擁有阻止所有工作線程的資源,則會(huì)導(dǎo)致死鎖。例如,會(huì)話 S1 啟動(dòng)事務(wù)并在行 r1 上獲取共享 (S) 鎖,然后進(jìn)入睡眠狀態(tài)。在所有可用工作線程上運(yùn)行的活動(dòng)會(huì)話正在嘗試獲取行 r1 上的獨(dú)占 (X) 鎖。由于會(huì)話 S1 無(wú)法獲取工作線程,因此它無(wú)法提交事務(wù)并釋放行 r1 上的鎖。這會(huì)導(dǎo)致死鎖。
內(nèi)存。當(dāng)并發(fā)請(qǐng)求正在等待可用內(nèi)存無(wú)法滿足的內(nèi)存授予時(shí),可能會(huì)發(fā)生死鎖。例如,兩個(gè)并發(fā)查詢 Q1 和 Q2 作為用戶定義的函數(shù)執(zhí)行,分別獲取 10 MB 和 20 MB 的內(nèi)存。如果每個(gè)查詢需要 30 MB,總可用內(nèi)存為 20 MB,則 Q1 和 Q2 必須等待對(duì)方釋放內(nèi)存,這會(huì)導(dǎo)致死鎖。
與并行查詢執(zhí)行相關(guān)的資源。與交換端口關(guān)聯(lián)的協(xié)調(diào)器、生產(chǎn)者或使用者線程可能會(huì)相互阻塞,從而導(dǎo)致死鎖,通常是在包含至少一個(gè)不屬于并行查詢一部分的其他進(jìn)程時(shí)。此外,當(dāng)并行查詢開(kāi)始執(zhí)行時(shí),SQL Server 會(huì)根據(jù)當(dāng)前工作負(fù)荷確定并行度或工作線程數(shù)。如果系統(tǒng)工作負(fù)荷意外更改(例如,新查詢開(kāi)始在服務(wù)器上運(yùn)行或系統(tǒng)工作線程不足),則可能會(huì)發(fā)生死鎖。
多個(gè)活動(dòng)結(jié)果集 (MARS) 資源。用戶資源、會(huì)話互斥鎖、事務(wù)互斥鎖等這些資源用于控制 MARS 下多個(gè)活動(dòng)請(qǐng)求的交錯(cuò)。
為了使任務(wù)在 MARS 下運(yùn)行,它必須獲取會(huì)話互斥鎖。如果任務(wù)在事務(wù)下運(yùn)行,則必須獲取事務(wù)互斥鎖。這保證在給定會(huì)話和給定事務(wù)中一次只有一個(gè)任務(wù)處于活動(dòng)狀態(tài)。獲取所需的互斥鎖后,任務(wù)就可以執(zhí)行。當(dāng)任務(wù)完成或在請(qǐng)求中間產(chǎn)生時(shí),它將首先釋放事務(wù)互斥鎖,然后以相反的獲取順序釋放會(huì)話互斥鎖。但是,這些資源可能會(huì)發(fā)生死鎖。在以下偽代碼中,兩個(gè)任務(wù)(用戶請(qǐng)求 U1 和用戶請(qǐng)求 U2)在同一會(huì)話中運(yùn)行。
從用戶請(qǐng)求 U1 執(zhí)行的存儲(chǔ)過(guò)程已獲取會(huì)話互斥鎖。如果存儲(chǔ)過(guò)程需要很長(zhǎng)時(shí)間才能執(zhí)行,則 SQL Server 數(shù)據(jù)庫(kù)引擎假定存儲(chǔ)過(guò)程正在等待用戶的輸入。用戶請(qǐng)求 U2 正在等待會(huì)話互斥鎖,而用戶正在等待來(lái)自 U2 的結(jié)果集,U1 正在等待用戶資源。這是死鎖狀態(tài),邏輯上說(shuō)明為:
三、處理死鎖
當(dāng) SQL Server 數(shù)據(jù)庫(kù)引擎實(shí)例選擇某個(gè)事務(wù)作為死鎖受害者時(shí),它將終止當(dāng)前批處理,回滾該事務(wù),并向應(yīng)用程序返回錯(cuò)誤消息 1205。
由于提交 Transact-SQL 查詢的任何應(yīng)用程序都可以被選為死鎖受害者,因此應(yīng)用程序應(yīng)具有可以捕獲錯(cuò)誤消息 1205 的錯(cuò)誤處理程序。如果應(yīng)用程序未捕獲錯(cuò)誤,則應(yīng)用程序可以在不知道其事務(wù)已回滾并可能發(fā)生錯(cuò)誤的情況下繼續(xù)操作。
實(shí)現(xiàn)捕獲錯(cuò)誤消息 1205 的錯(cuò)誤處理程序允許應(yīng)用程序處理死鎖情況并采取補(bǔ)救措施(例如,自動(dòng)重新提交死鎖中涉及的查詢)。通過(guò)自動(dòng)重新提交查詢,用戶無(wú)需知道發(fā)生了死鎖。
在重新提交查詢之前,應(yīng)用程序應(yīng)短暫暫停。這使死鎖中涉及的其他事務(wù)有機(jī)會(huì)完成并釋放構(gòu)成死鎖循環(huán)一部分的鎖。這樣可以最大程度地降低在重新提交的查詢請(qǐng)求其鎖時(shí)再次發(fā)生死鎖的可能性。
四、最大限度地減少死鎖
盡管無(wú)法完全避免死鎖,但遵循某些編碼約定可以最大程度地減少生成死鎖的可能性。最小化死鎖可以提高事務(wù)吞吐量并減少系統(tǒng)開(kāi)銷,因?yàn)槭聞?wù)較少:
- 回滾,撤消事務(wù)執(zhí)行的所有工作。
- 由應(yīng)用程序重新提交,因?yàn)樗鼈冊(cè)谒梨i時(shí)已回滾。
要幫助最大程度地減少死鎖,請(qǐng)執(zhí)行以下操作:
- 以相同的順序訪問(wèn)對(duì)象。
- 避免事務(wù)中的用戶交互;保持交易簡(jiǎn)短且批量。
- 使用較低的隔離級(jí)別。
- 使用基于行版本控制的隔離級(jí)別。將數(shù)據(jù)庫(kù)選項(xiàng)設(shè)置為啟用已提交的讀取事務(wù)以使用行版本控;使用快照隔離。
- 使用綁定連接。
4.1、以相同的順序訪問(wèn)對(duì)象
如果所有并發(fā)事務(wù)都以相同的順序訪問(wèn)對(duì)象,則不太可能發(fā)生死鎖。例如,如果兩個(gè)并發(fā)事務(wù)在表上獲取鎖,然后在表上獲得鎖,則一個(gè)事務(wù)在表上被阻止,直到另一個(gè)事務(wù)完成。第一個(gè)事務(wù)提交或回滾后,第二個(gè)事務(wù)將繼續(xù),并且不會(huì)發(fā)生死鎖。對(duì)所有數(shù)據(jù)修改使用存儲(chǔ)過(guò)程可以標(biāo)準(zhǔn)化訪問(wèn)對(duì)象的順序。
4.2、避免事務(wù)中的用戶交互
避免編寫包含用戶交互的事務(wù),因?yàn)闊o(wú)需用戶干預(yù)即可運(yùn)行的批處理的速度比用戶必須手動(dòng)響應(yīng)查詢(例如回復(fù)應(yīng)用程序請(qǐng)求的參數(shù)提示)的速度快得多。例如,如果事務(wù)正在等待用戶輸入,并且用戶周末去吃午飯甚至回家,則用戶會(huì)延遲事務(wù)完成。這會(huì)降低系統(tǒng)吞吐量,因?yàn)槭聞?wù)持有的任何鎖僅在提交或回滾事務(wù)時(shí)釋放。即使沒(méi)有出現(xiàn)死鎖情況,訪問(wèn)相同資源的其他事務(wù)也會(huì)在等待事務(wù)完成時(shí)被阻止。
4.3、保持交易簡(jiǎn)短且在一個(gè)批次中
當(dāng)多個(gè)長(zhǎng)時(shí)間運(yùn)行的事務(wù)在同一數(shù)據(jù)庫(kù)中同時(shí)執(zhí)行時(shí),通常會(huì)發(fā)生死鎖。事務(wù)時(shí)間越長(zhǎng),獨(dú)占鎖或更新鎖的持有時(shí)間就越長(zhǎng),從而阻止其他活動(dòng)并導(dǎo)致可能的死鎖情況。
將事務(wù)保存在一個(gè)批次中可最大程度地減少事務(wù)期間的網(wǎng)絡(luò)往返,從而減少完成事務(wù)和釋放鎖定時(shí)可能出現(xiàn)的延遲。
4.4、使用較低的隔離級(jí)別
確定事務(wù)是否可以在較低的隔離級(jí)別運(yùn)行。實(shí)現(xiàn) read commit 允許事務(wù)讀取先前由另一個(gè)事務(wù)讀?。ㄎ葱薷模┑臄?shù)據(jù),而無(wú)需等待第一個(gè)事務(wù)完成。與較高的隔離級(jí)別(如可序列化)相比,使用較低的隔離級(jí)別(如已提交的讀取)保留共享鎖的持續(xù)時(shí)間更短。這減少了鎖定爭(zhēng)用。
4.5、使用基于行版本控制的隔離級(jí)別
當(dāng)數(shù)據(jù)庫(kù)選項(xiàng)設(shè)置為 ON 時(shí),在讀取提交隔離級(jí)別下運(yùn)行的事務(wù)在讀取操作期間使用行版本控制而不是共享鎖。
快照隔離還使用行版本控制,在讀取操作期間不使用共享鎖。在事務(wù)可以在快照隔離下運(yùn)行之前,必須設(shè)置數(shù)據(jù)庫(kù)選項(xiàng)ALLOW_SNAPSHOT_ISOLATIONON。
實(shí)現(xiàn)這些隔離級(jí)別,以最大程度地減少讀取和寫入操作之間可能發(fā)生的死鎖。
4.6、使用綁定連接
使用綁定連接,同一應(yīng)用程序打開(kāi)的兩個(gè)或多個(gè)連接可以相互協(xié)作。輔助連接獲取的任何鎖都像由主連接獲取一樣保存,反之亦然。因此,它們不會(huì)相互阻塞。
4.7、停止事務(wù)
在死鎖方案中,受害事務(wù)會(huì)自動(dòng)停止并回滾。在死鎖情況下無(wú)需停止事務(wù)。
總結(jié)
有些應(yīng)用程序依賴于讀取提交隔離的鎖定和阻止行為。對(duì)于這些應(yīng)用程序,在啟用使用基于行版本控制的隔離級(jí)別之前需要進(jìn)行一些更改。
到此這篇關(guān)于SQL Server數(shù)據(jù)庫(kù)的死鎖詳細(xì)說(shuō)明的文章就介紹到這了,更多相關(guān)SQL Server死鎖內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java 實(shí)現(xiàn)連接sql server 2000
JDBC技術(shù)事實(shí)上是一種能通過(guò)JAVA語(yǔ)言訪問(wèn)任何結(jié)構(gòu)化數(shù)據(jù)庫(kù)的應(yīng)用程序接口(API)(Sun這樣說(shuō)的,我也不知道是不是真的),而且現(xiàn)在的JDBC 3.0據(jù)Sun說(shuō)也能訪問(wèn)Execel等電子表格程序!2009-06-06MSSQL監(jiān)控?cái)?shù)據(jù)庫(kù)的DDL操作(創(chuàng)建,修改,刪除存儲(chǔ)過(guò)程,創(chuàng)建,修改,刪除表等)
下面就是一個(gè)解決上述問(wèn)題的方案,我們通過(guò)創(chuàng)建一個(gè)表DatabaseLog和DDL觸發(fā)器來(lái)解決問(wèn)題,首先在msdb數(shù)據(jù)庫(kù)里面新建一個(gè)表DatabaseLog,用來(lái)保存DDL觸發(fā)器獲取的信息2013-08-08基于Sql Server通用分頁(yè)存儲(chǔ)過(guò)程的解決方法
本篇文章,小編將為大家介紹基于Sql Server通用分頁(yè)存儲(chǔ)過(guò)程的解決方法,有需要的朋友可以參考一下2013-04-04SqlServer數(shù)據(jù)庫(kù)中文亂碼問(wèn)題解決方法
SQL版的亂碼問(wèn)題還是出現(xiàn)在SQL SERVER的安裝設(shè)置上。默認(rèn)安裝時(shí)系統(tǒng)默認(rèn)的排序規(guī)則是拉丁文的排序規(guī)則,但一般人在安裝時(shí)沒(méi)有考慮到這一點(diǎn),安裝時(shí)只是點(diǎn)取下一步,安裝完成后,造成了SQL版在使用過(guò)程中出現(xiàn)亂碼2020-03-03數(shù)據(jù)庫(kù)存儲(chǔ)過(guò)程分頁(yè)顯示
數(shù)據(jù)庫(kù)存儲(chǔ)過(guò)程分頁(yè)顯示...2006-08-08SQL Server中使用Trigger監(jiān)控存儲(chǔ)過(guò)程更改腳本實(shí)例
這篇文章主要介紹了SQL Server中使用Trigger監(jiān)控存儲(chǔ)過(guò)程更改腳本實(shí)例,本文使用一個(gè)表來(lái)記錄存儲(chǔ)過(guò)程更改過(guò)程,需要的朋友可以參考下2015-07-07SQLServer日志清空語(yǔ)句(sql2000,sql2005,sql2008)
時(shí)候當(dāng)系統(tǒng)運(yùn)行時(shí)間比較長(zhǎng)的時(shí)候,我們把備份的數(shù)據(jù)庫(kù)還原的時(shí)候發(fā)現(xiàn),數(shù)據(jù)庫(kù)中數(shù)據(jù)文件和日志文件變的好大,特別是日志文件。現(xiàn)在給大家介紹如何清理SQLServer數(shù)據(jù)庫(kù)日志2013-05-05