国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
快速搞懂 SQL Server 的鎖定和阻塞
2010-08-14 17:08

本帖提供兩種做法,可避免在 SQL Server 事務鎖定時產(chǎn)生的不正?;蜷L時間阻塞,讓用戶和程序也無限期等待,甚至引起 connection pooling 連接數(shù)超過容量。

所謂的「阻塞」,是指當一個數(shù)據(jù)庫會話中的事務,正在鎖定其他會話事務想要讀取或修改的資源,造成這些會話發(fā)出的請求進入等待的狀態(tài)。SQL Server 默認會讓被阻塞的請求無限期地一直等待,直到原來的事務釋放相關的鎖,或直到它超時 (根據(jù) SET LOCK_TIMEOUT,本文后續(xù)會提到)、服務器關閉、進程被殺死。一般的系統(tǒng)中,偶爾有短時間的阻塞是正常且合理的;但若設計不良的程序,就可能導致長時間的阻塞,這樣就不必要地鎖定了資源,而且阻塞了其他會話欲讀取或更新的需求。遇到這種情況,可能就需要手工排除阻塞的狀態(tài),而本文接下來要介紹兩種排除阻塞的做法。

日前公司 server-side 有組件,疑似因撰寫時 exception-handling 做得不周全,導致罕見的特殊例外發(fā)生時,讓 SQL Server 的事務未執(zhí)行到 cmmmit 或 rollback,造成某些表或記錄被「鎖定 (lock)」。后來又有大量的 request,要透過代碼訪問這些被鎖定的記錄,結(jié)果造成了嚴重的長時間「阻塞」,最后有大量 process (進程) 在 SQL Server 呈現(xiàn)「等待中 (WAIT)」的狀態(tài)。

由于 SQL Server 的「事務隔離級別」默認是 READ COMMITTED (事務期間別人無法讀取),加上 SQL Server 的鎖定造成阻塞時,默認是別的進程必須無限期等待 (LOCK_TIMEOUT = -1)。結(jié)果這些大量的客戶端 request 無限期等待永遠不會提交或回滾的事務,并一直占用著 connection pool 中的資源,最后造成 connection pooling 連接數(shù)目超載。


查了一些書,若我們要查詢 SQL Server 目前會話中的 lock 超時時間,可用以下的命令:

SELECT @@LOCK_TIMEOUT


執(zhí)行結(jié)果默認為 -1,意即欲訪問的對象或記錄被鎖定時,會無限期等待。若欲更改當前會話的此值,可用下列命令:

SET LOCK_TIMEOUT 3000

后面的 3000,其單位為毫秒,亦即會先等待被鎖定的對象 3 秒鐘。若事務仍未釋放鎖,則會拋回如下代號為 1222 的錯誤信息,可供程序員編程時做相關的逾時處理:

消息 1222,級別 16,狀態(tài) 51,第 3 行
已超過了鎖請求超時時段。 若將 LOCK_TIMEOUT 設置為 0,亦即當欲訪問對象被鎖定時,完全不等待就拋回代號 1222 的錯誤信息。此外,此一 SET LOCK_TIMEOUT 命令,影響范例只限當前會話 (進程),而非對某個表做永久的設置。

-------------------------------------------------------------------------------------------


接下來我們在 SSMS 中,開兩個會話 (查詢窗口) 做測試,會話 A 創(chuàng)建會造成阻塞的事務進程,會話 B 去訪問被鎖定的記錄。

--會話 A
BEGIN TRAN;
UPDATE Orders SET EmployeeID=7 WHERE OrderID=10248
--rollback; --故意不提交或回滾

--會話 B
SELECT * FROM Orders WHERE OrderID=10248

分別執(zhí)行后,因為欲訪問的記錄是同一條,按照 SQL Server 「事務隔離級別」和「鎖」的默認值,會話 B 將無法讀取該條數(shù)據(jù),而且會永遠一直等下去 (若在現(xiàn)實項目里寫出這種代碼,就準備被客戶和老板臭罵)。

-------------------------------------------------------------------------------------------


若將會話 B 先加上 SET LOCK_TIMEOUT 3000 的設置,如下,則會話 B 會先等待 3 秒鐘,才拋出代號 1222 的「鎖請求已超時」錯誤信息:

--會話 B
SET
LOCK_TIMEOUT 3000
SELECT * FROM Orders WHERE OrderID=10248
--SET LOCK_TIMEOUT -1

執(zhí)行結(jié)果:

消息 1222,級別 16,狀態(tài) 51,第 3 行
已超過了鎖請求超時時段。
語句已終止。

-------------------------------------------------------------------------------------------


另根據(jù)我之前寫的文章「30 分鐘快快樂樂學 SQL Performance Tuning」所述:
http://www.cnblogs.com/WizardWu/archive/2008/10/27/1320055.html

撰寫不當?shù)?SQL 語句,會讓數(shù)據(jù)庫的索引無法使用,造成全表掃描或全聚集索引掃描。例如不當?shù)模篘OT、OR 算符使用,或是直接用 + 號做來串接兩個字段當作 WHERE 條件,都可能造成索引失效,變成全表掃描,除了性能變差之外,此時若這句不良的 SQL 語句,是本帖前述會話 B 的語句,由于會造成全表掃描或聚集索引掃描,因此就一定會被會話 A 的事務阻塞 (因為掃描全表時,一定也會讀到 OrderID=10248 這一條會話 A 正在鎖定的記錄)。


下方的 SQL 語句,由于 OrderID 字段有設索引,因此下圖 1 的「執(zhí)行計劃」,會以算法中的「二分查找法」在索引中快速查找 OrderID=10250 的記錄。

SELECT * FROM Orders WHERE OrderID=10250

SELECT * FROM Orders WHERE OrderID=10250 AND ShipCountry='Brazil'



圖 1 有正確使用到索引的 SQL 語句,以垂直的方向使用索引。用 AND 算符時,只要有任一個字段有加上索引,就能受惠于索引的好處,并避免全表掃描 此時若我們將這句 SQL 語句,當作前述會話 B 的語句,由于它和會話 A 所 UPDATE 的 OrderID=10248 不是同一條記錄,因此不會受會話 A 事務未回滾的影響,會話 B 能正常執(zhí)行 SELECT 語句。


但若我們將會話 B 的 SQL 語句,改用如下的 OR 算符,由于 ShipCountry 字段沒有加上索引,此時會造成聚集索引掃描 (和全表掃描一樣,會對整個表做逐條記錄的 scan)。如此一來,除了性能低落以外,還會因為在逐條掃描時,讀到會話 A 中鎖定的 OrderID=10248 那一條記錄,造成阻塞,讓會話 B 永遠呈現(xiàn)「等待中」的狀態(tài)。

SELECT * FROM Orders WHERE OrderID=10250 OR ShipCountry='Brazil'



圖 2 未正確使用索引的 SQL 語句,以水平的方向使用索引。用 OR 算符時,必須「所有」用到的字段都有加上索引,才能有效使用索引、避免全表掃描

-------------------------------------------------------------------------------------------


發(fā)生阻塞時,透過以下命令,可看出是哪個進程 session id,阻塞了哪幾個進程 session id,且期間經(jīng)過了多少「毫秒 (ms)」。如下圖 3 里 session id = 53 阻塞了 session id = 52 的進程。另透過 SQL Server Profiler 工具,也能看到相同的內(nèi)容。

SELECT blocking_session_id, wait_duration_ms, session_id FROM sys.dm_os_waiting_tasks



圖 3 本帖前述會話 A 的 UPDATE 語句 (53),阻塞了會話 B 的 SELECT 語句 (52)


透過以下兩個命令,我們還能看到整個數(shù)據(jù)庫的鎖定和阻塞詳細信息:

SELECT * FROM sys.dm_tran_locks

EXEC sp_lock



圖 4 session id = 52 的 process 因阻塞而一直處于等待中 (WAIT)


另透過 KILL 命令,可直接殺掉造成阻塞的 process,如下:

KILL 53

-------------------------------------------------------------------------------------------


欲解決無限期等待的問題,除了前述的 SET LOCK_TIMEOUT 命令外,還有更省事的做法,如下,在會話 B 的 SQL 語句中,在表名稱后面加上 WITH (NOLOCK) 關鍵字,表示要求 SQL Server,不必去考慮這個表的鎖定狀態(tài)為何,因此也可減少「死鎖 (dead lock)」發(fā)生的機率。但 WITH (NOLOCK) 不適用 INSERT、UPDATE、DELETE。

SELECT * FROM Orders WITH (NOLOCK) WHERE OrderID=10248


類似的功能,也可如下,在 SQL 語句前,先設置「事務隔離級別」為可「臟讀 (dirty read)」。

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
SELECT * FROM Orders WHERE OrderID=10248


兩種做法的效果類似,讓會話 B 即使讀到被鎖阻塞的記錄,也永遠不必等待,但可能讀到別人未提交的數(shù)據(jù)。雖然說這種做法讓會話 B 不用請求共享鎖,亦即永遠不會和其他事務發(fā)生沖突,但應考慮項目開發(fā)實際的需求,若會話 B 要查詢的是原物料的庫存量,或銀行系統(tǒng)的關鍵數(shù)據(jù),就不適合用這種做法,而應改用第一種做法的 SET LOCK_TIMEOUT 命令,明確讓數(shù)據(jù)庫拋回等候逾時的錯誤代號 1222,再自己寫代碼做處理。


-------------------------------------------------------------------------------------------


歸根究柢,我們在編程時,就應該避免寫出會造成長時間阻塞的 SQL 語句,亦即應最小化鎖定爭用的可能性,以下為一些建議:

  • 盡可能讓事務輕薄短小、讓鎖定的時間盡量短,例如把不必要的命令移出事務外,或把一個大量更新的事務,切成多個更新較少的事務,以改善并發(fā)性。
  • 將組成事務的 SQL 語句,擺到一個「批 (batch) 處理」,以避免不必要的延遲。這些延遲常由 BEGIN TRAN ... COMMIT TRAN 命令之間的網(wǎng)絡 I/O 所引起。
  • 考慮將事務的 SQL 語句寫在一個存儲過程內(nèi)。一般來說,存儲過程的執(zhí)行速度會比批處理的 SQL 語句快,且存儲過程可降低網(wǎng)絡的流量和 I/O,讓事務可更快完成。
  • 盡可能頻繁地認可 Cursor 中的更新,因為 Cursor 的處理速度較慢,會讓鎖定的時間較長。
  • 若無必要,使用較寬松的事務隔離級別,如前述的 WITH (NOLOCK) 和 READ UNCOMMITTED。而非為了項目開發(fā)方便,全部使用默認的 READ COMMITTED 級別。
  • 避免在事務執(zhí)行期間,還要等待用戶的反饋或交互,這樣可能會造成無限期的持有鎖定,如同本帖一開始提到的狀況,最后造成大量的阻塞和數(shù)據(jù)庫 connection 被占用。
  • 避免事務 BEGIN TRAN 后查詢的數(shù)據(jù),可能在事務開始之前先被引用。
  • 避免在查詢時 JOIN 過多的表 (此指非必要的 JOIN),否則除了性能較差外,也很容易讀到正被鎖定或阻塞中的表和字段。
  • 應注意在一個沒有索引的表上,過量的「行鎖」,或一些鎖定使用了過多的內(nèi)存和系統(tǒng)資源時,SQL Server 為了有效地管理這些鎖定,會嘗試將鎖定擴展為整個表的「表鎖」,此時會很容易造成其他 process 在訪問時的阻塞和等待。

-------------------------------------------------------------------------------------------

本帖尚未提到死鎖和其他更進階的議題,等下次有空再繼續(xù)泡茶聊天。  

本站僅提供存儲服務,所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權內(nèi)容,請點擊舉報。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
ORACLE鎖
關于查詢MySQL事務阻塞信息,你還可以這樣玩?
SQL Server查詢使用鍵查找時鎖申請及釋放順序
盤點那些被問爛了的 Mysql 面試題 | Laravel China 社區(qū)
6. 鎖
深入理解 SQL Server 2008 的鎖機制
更多類似文章 >>
生活服務
分享 收藏 導長圖 關注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點擊這里聯(lián)系客服!

聯(lián)系客服