運行緩慢的查詢語句(阻塞)(一)--鎖粒度及鎖升級


這些天看了一篇微軟官方發布的MS SQL Server2008性能問題處理及優化的英文文檔,里面知識點介紹地很詳細,在現實工作中也很實用,遂產生了想把它翻譯一下的念頭。翻譯的過程,既可以幫助自己復習一下這些技術,也可以向其他還不熟悉這一塊的朋友介紹一些新的知識,何樂而不為呢。只是這篇文章有點長,我會分成幾篇隨筆去介紹,所以,不光是對我耐性的考驗,也是對你的考驗哦!

 

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

  

  運行緩慢或者持續運行很長時間的查詢會導致過度的資源消耗。它們可以導致查詢阻塞的嚴重后果。

  過度的資源消耗不局限於只占用CPU資源,也會增加I/O帶寬以及內存帶寬。盡管SQL Server查詢可以通過where謂詞的限制避免進行全表掃描,但是由於缺少支持這個特定查詢所需要的索引,所以他們可能不會按照預期的方式執行。同樣,where謂詞頁可以由應用程序動態進行構建,或者依賴於用戶的輸入。綜上,已經存在的索引不能覆蓋所有可能出現的限制。T-SQL語句可能會導致的過度的CPU,I/O,以及內存消耗的情況,在前面的章節中已經有所介紹。

  此外,沒有使用索引的話,可能是因為存在索引只是沒有被使用。因為所有的索引都不得不被維護,這可能不會影響到查詢的性能,但是會影響到那些DML的查詢。

  查詢運行緩慢也可以由邏輯鎖等待或者系統資源阻塞查詢而導致。導致阻塞的原因可以是設計不好的應用程序,不好的查詢計划,缺失有效的索引,或者是相對於當前負載配置不足的sql 實例。

  這一章專注於導致運行緩慢的兩種原因:阻塞和索引問題。

阻塞

  阻塞主要是為了等待邏輯鎖,例如等待獲取加在資源上的X鎖(排他鎖)或者等待來在於較低級別同步基元的結果,例如閂鎖。

  邏輯鎖等待發生在某個請求想要在一個已經被加過鎖的資源上加上另一個不兼容的鎖時。盡管這需要在這個特定的正在運行的查詢語句中添加保證數據一致性的事務隔離級別,但是這會讓用戶有一種SQL Server運行很緩慢的感覺。當一個查詢阻塞之后,它就不會在消耗任何的系統資源了,所以你會發現運行速度很慢,但是占用的系統資源很少。

  如果你的系統沒有被配置成可以處理當前的系統負載,那么會導致等待較低級別的同步基元。

  阻塞及等待的通用場景如下:

  --確定阻塞者

  --確定長的阻塞

  --每個對象對應的阻塞

  --頁閂問題

  --使用SQL Server等待造成的阻塞對性能影響的縱覽

 

  如果當前系統資源不能為請求提供服務時,一個SQL Server會話被放置在一個等待狀態里。換句話說,等待會在一隊請求都在請求資源時出現。DMVs可以提供任何等待資源的會話信息。

  SQL Server 2008提供詳細且連續的等待信息,報告大概會有125中等待類型。這些DMVs從sys.dm_os_wait_statisticssys.dm_os_waiting_tasks中獲取這些信息,sys.dm_os_wait_statistics顯示SQL Server全部或者逐漸積累的等待信息,sys.dm_os_waiting_tasks是指定在會話中的,它會按照會話阻斷等待。下面的DMV提供了等待隊列中的所有正在等待資源的任務。它會即時顯示系統中等待隊列中的所有任務。例如,你可以使用下面查詢查找56號阻塞會話的詳細信息.

select * from sys.dm_os_waiting_tasks where session_id=56

 

下面的結果顯示出了56號會話正在被53號會話阻塞,並且56號會話已經耗費了1,03,500毫秒去等待一個鎖。

    • waiting_task_address: 0x022A8898
    • session_id: 56
    • exec_context_id: 0
    • wait_duration_ms: 1103500
    • wait_type: LCK_M_S                                                  
    • resource_address: 0x03696820  
    • blocking_task_address: 0x022A8D48
    • blocking_session_id: 53
    • blocking_exec_context_id: NULL
    • resource_description: ridlock fileid=1 pageid=143 dbid=9 id=lock3667d00 mode=X
      associatedObjectId=72057594038321152

為了去查找有哪些會話已經被授權了鎖或者正在等待鎖,你可以使用sys.dm_tran_locks視圖。所有的行展示了當前所有活動的請求,哪些已經被授權鎖,哪些因為資源上已經加了鎖所以正在等待授權鎖。對於有規律的鎖而言,一個已經被授權的請求會顯示出已經為這個請求在這個資源上授權了鎖。一個等待的請求說明還沒有在資源上為這個請求授權鎖。例如,下面的查詢及輸出顯示了56號會話在資源1:143:3上的請求被53號會話已經授權的X鎖阻塞了。

select 
    request_session_id as spid, 
    resource_type as rt,  
    resource_database_id as rdb, 
    (case resource_type
      WHEN 'OBJECT' then object_name(resource_associated_entity_id)
      WHEN 'DATABASE' then ' '
      ELSE (select object_name(object_id) 
            from sys.partitions 
            where hobt_id=resource_associated_entity_id)
    END) as objname, 
    resource_description as rd,  
    request_mode as rm, 
    request_status as rs
from sys.dm_tran_locks

  下面是示例輸出:

spid     rt           rdb         objname       rd            rm           rs

-----------------------------------------------------------------------------                                                                                                        56    DATABASE     9                               S          GRANT

53    DATABASE     9                              S          GRANT

56    PAGE          9       t_lock      1:143       IS         GRANT

53    PAGE        9       t_lock      1:143       IX         GRANT

53    PAGE          9       t_lock      1:153       IX         GRANT

56   OBJECT       9       t_lock                  IS         GRANT

53   OBJECT       9        t_lock                 IX         GRANT

53    KEY         9        t_lock      (a400c34cb X          GRANT

53    RID         9        t_lock      1:143:3    X          GRANT

56    RID         9        t_lock      1:143:3    S       WAIT

 

 

鎖粒度及鎖升級

  理解阻塞的一個關鍵點是理解事務隔離級別及鎖升級。在所有事務隔離級別下,事務隔離級別只會干預處於S鎖模式下的事務卻不會去影響處於X鎖模式的事務。鎖的粒度決定了鎖的影響級別,是在行上,還是在頁級別,還是在表級別。很明顯,鎖的粒度越高,並發性也就越低。例如,如果一個事務在表級別上使用X鎖無修改某一行的數據,它會阻塞其它想要讀取或修改其它行的事務。反之,高粒度鎖的好處是SQL Server無需獲得如此多的鎖。鎖粒度是由SQL Server使用啟發模式自行管理的,但是這里也會有一些情況可能會與預期的不一致。例如這種情況,用戶可以使用sp_tableoption或者alter index ddl去控制對象上的鎖粒度或者使用鎖定提示(locking hints)。

--對於鎖,還有另外一個很有意思的扭轉。如果在運行時,SQL Server發現語句中加載到某個對象上的鎖的數量已經超過了閾值,那么可以對這個表上的鎖進行升級。鎖升級會在下面的情況發生時被觸發:

  1. 在事務的某一單獨語句中,需要在兩個索引或者兩個堆上分別使用2500個鎖
  2. 在事務的某一單獨語句中,需要在非聚集索引上加2500個鎖並且需要在對應的基表上加2500個鎖
  3. 在一個語句中,同一個堆或者索引被引用超過了兩次;在同一個實例上,像這樣的鎖實際上會被分開計數,造成了重復。所以比如,要在table1上使用self-join,如果每個實例在這個語句中會有3000個鎖,鎖升級就不會被觸發

--如果鎖配置選項被設定成了默認值0,那么被鎖定的資源會比non-AWE(32-bit)或者正常的(64-bit)的多占用40%的內存。在這種情況下,鎖定的內存是動態進行分配的。

當鎖升級被觸發以后,SQL Server嘗試去升級鎖到表級別,但是如果有沖突的鎖存在時,這種嘗試就會失敗。所以比如,如果S鎖需要升級至表級別並且當前同時在行上或者頁上存在着X鎖,鎖嘗試升級就會失敗。然而,SQL Server會周期性地,每當有1250(通常情況下)個新的鎖被鎖所有者(也就是說,事務初始化這些鎖)申請之后,就會嘗試性地去升級鎖。如果鎖升級成功的話,SQL Server就會釋放較低粒度的鎖,以及索引及堆所對應鎖定的內存。因為在鎖升級期間,不能有任何沖突性質的訪問,一個成功的鎖升級可能會影響接下來鎖模式相沖突的事務處理,並導致出現阻塞。也就意味着,鎖升級並不總是一個針對所有應用程序的好辦法。

禁用鎖升級

SQL Server提供了下面的追蹤標識去禁用鎖升級:

--TraceFlag-1211:在每一個索引,每一個對或者每一個語句的基礎上,當超過了閾值(5000)時,鎖升級就會被禁用掉。如果這個追蹤標識起作用的話,鎖就永遠不會被升級。這個追蹤標識也會指引SQL Server忽略掉鎖管理器已經申請到的內存,從而使內存達到一個最大的靜態分配內存或non-AWE(32-bit)的60%,或者是正常的(64-bit)動態分配的內存。這時,一個out-of-lock-memory錯誤會被生成。這些是存在着潛在威脅的,因為一個應用程序會申請大量的鎖緊而耗費掉SQL Server的內存。這也就是說,在這種最壞的情況,會使SQL Server的性能處於一個非正常的水平。對於這些因素,你必須非常謹慎地使用追蹤標識。

--TraceFlag-1224:這個追蹤標識跟1211號追蹤標識很像,但也存在着一個很關鍵的不同。當鎖管理器申請40%的靜態分配內存或者40%的non-AWE(32-bit)或者正常的動態分配分配內存(64-bit)時,它就會啟用鎖升級。此外,如果這些內存因為其它的組件正在使用更多的內存而不能被分配,鎖升級就會被提前觸發。SQL Server會生成一個內存溢出的錯誤,當內存超過了靜態分配內存或者60%的nonAWE(32-bit)或者正常的動態分配內存(64-bit)。

  如果兩個追蹤標識(1211和1224)同時被設定,1211會被優先使用。你可以使用DBCC TRACESTATUS(-1)命令去查找SQL Server中所有被啟用的追蹤標識的狀態。這些追蹤標識的限制是它們的粒度很粗(也就是說,它們是在SQL Server實例的級別的)。SQL Server通過在表級別提供一個選項去控制啟用或者禁用鎖升級。第二,SQL Server2008在分區表級別提供鎖升級,這樣做的目的是防止DML操作不會影響其他的分區。

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM