1. 程式人生 > 資料庫 >SQL Server中的SELECT會阻塞SELECT嗎

SQL Server中的SELECT會阻塞SELECT嗎

前言

在SQL Server中,我們知道一個SELECT語句執行過程中只會申請一些意向共享鎖(IS) 與共享鎖(S),例如我使用SQL Profile跟蹤會話86執行SELECT * FROM dbo.TEST WHERE OBJECT_ID =1 這個查詢語句,其申請、釋放的鎖資源的過程如下所示:

而且從最常見的鎖模式的相容性表,我們可以看到IS鎖與S鎖都是相容的,也就是說SELECT查詢是不會阻塞SELECT查詢的。

現有的授權模式

請求的模式

IS

S

U

IX

SIX

X

意向共享(IS)

共享(S)

更新(U)

意向排他(IX)

意向排他共享(SIX)

排他(X)

但是在某些特殊場景。你會看到SELECT語句居然“阻塞”SELECT操作,那麼SQL Server中SELECT會真的阻塞SELECT操作嗎?我們先構造測試的案例場景,那麼先準備測試資料吧

CREATE TABLE TEST (OBJECT_ID INT,NAME VARCHAR(8));
 
CREATE INDEX PK_TEST ON TEST(OBJECT_ID) 
 
 
DECLARE @Index INT =0;
 
WHILE @Index < 20
BEGIN
 INSERT INTO TEST
 SELECT @Index,'kerry';
 
 SET @Index = @Index +1;
END

在會話視窗A中,執行下面SQL語句,模擬一個UPDATE語句正在執行

BEGIN TRANSACTION
 
 UPDATE dbo.TEST SET NAME='Kerry' WHERE OBJECT_ID=1;
 --ROLLBACK;

會話視窗B中,執行下面的SQL語句

SELECT * FROM dbo.TEST WHERE OBJECT_ID=1

會話視窗C中,執行下面的SQL語句

SELECT * FROM dbo.TEST WHERE OBJECT_ID=1

我實驗的場景下,會話視窗A的會話ID為85,會話視窗B的會話ID為90,會話視窗C的會話ID為87,如下所示

如下所示,你會看到SELECT語句“阻塞”了SELECT語句,即會話90“阻塞”了會話87, 它們的等待事件都為LCK_M_S,也就是說它們都在等待獲取共享鎖,也許你會置疑這個SQL是否有問題,那麼我們使用SP_WHO來檢視,你會發現也是如此,如下所示:

如下所示,我們會發現會話ID為90 、87的會話都在等待型別為RID,Resource為1:24171:1的共享鎖

其實應該說,會話87、90都在等待RID物件的共享鎖,我們知道共享鎖與意向共享鎖都是相容的,所以SELECT是不會阻塞SELECT的,那麼又怎麼解釋這個現象呢?在宋大神的指點下,粗略的翻了Database System Implementaion這本書(很多原理性知識,看起來相當吃力)。裡面介紹了在鎖表(lock table)以及Element Info、Handling Lock Requests、Handling Unlocks等概念,有一個有意思的圖所示,

在鎖表(lock table)裡,elements info裡的鎖的申請是在一個類似佇列的結構。先進先出機制,所以當會話90先進入佇列,它在等待共享鎖(S),會話87也進入佇列等待共享鎖(S),而且它在會話90的後面(即會話90這個elements info後面的Next指標指向會話87會話的事務),由於兩個會話都被阻塞,這兩個會話的Wait欄位都是Yes,由於內部某些機制,會話87顯示阻塞它的會話為90(這個是我個人臆測,實際具體原因有待考究),實質阻塞的源頭還是會話85. 當會話85釋放排它鎖(X)後,會話佇列根據下面幾個原則來處理解鎖(Handling Unlocks):

1: First-come-first-served: Grant the lock request that has been waiting the longest. This strategy guarantees no starvation,the situation where a transaction can wait forever for a lock

先來先服務(佇列的原則):授予鎖等待時間最長的鎖請求,這種策略保證不會餓死(翻譯感覺不貼切),即一個事務不會永遠等待鎖的情況。

2. Priority to shared locks: First grant all the shared locks waiting. Then,grant one update lock,if there are any waiting. Only grant an exclusive lock if no others are waiting. This strategy can allow starvation,if a transaction is waiting for a U or X lock.

共享鎖優先,首先授予所有等待共享鎖(S),然後授予其中一個更新鎖(U),如果有其它型別等待,只有在沒有其它鎖等待時,才授予排它鎖、這一策略允許等待更新鎖或排它鎖的事務餓死(結束)

3. Priority to upgrading: If there is a transaction with a U lock waiting to upgrade it to an X lock,grant that first. Otherwise,follow one of the other strategies mentioned.

鎖升級優先,如果有一個持有共享鎖(U)等待升級Wie排他鎖(X),那麼先授予它排它鎖,否則採用前面已經提到的策略中的一個。

按照這些原則,當會話85釋放了排它鎖(X)後,排程器(Scheduler)應該會根據先後順序依次授予會話90、87共享鎖(S),兩者的阻塞會幾乎同時消失。 這個可以也可以通過實驗進行一個大概的推斷, 在上面實驗中,你可以手工取消90會話的查詢操作,然後再檢視阻塞情況,就會發現會話87被85阻塞了。這個阻塞的源頭就變成了85,而不是90了。

PS:上面是個人結合一些知識和理解,做的一些膚淺的判斷與分析,如果不對的地方,敬請指正!

參考資料:

Database System Implementaion

總結

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對我們的支援。