SQL Server中的SELECT會阻塞SELECT相關資料

佚名發表於2018-12-08
文章主要給大家介紹了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操作嗎?我們先構造測試的案例場景,那麼先準備測試資料吧

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

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

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

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

  1. SELECT * FROM dbo.TEST WHERE OBJECT_ID=1

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

  1. 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

總結

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,謝謝大家的支援。

相關文章