鎖升級(Lock Escalations)很是很是很差,由於最後你會是在表級別有排它或共享鎖。在表級別放上這樣的限制鎖會下降你的併發和數據庫的吞吐量。html
今天我不想討論鎖升級的基本信息,今天我想詳細談下當鎖升級觸發時,SQL Server的這種鎖行爲對執行計劃狀況的影響。sql
假設咱們有以下很是簡單的查詢:數據庫
1 SELECT * FROM Sales.SalesOrderDetail 2 WHERE ModifiedDate > '20200501' 3 GO
如你所見,我就從Sales.SalesOrderDetail表請求ModifiedDate晚於2020年5月1日的記錄。固然,這個查詢不會返回任何記錄,由於選擇的日期是未來的。性能優化
但當你查看執行計劃時,你會看到查詢優化器選擇了完整的彙集索引掃描運算符,緊接着是篩選運算符。併發
在執行計劃裏,一個完整的彙集索引掃描運算符,緊接着是一個篩選器運算符真的是一個很差的模式。它意味着一開始(在掃描運算符),你就讀取大量的數據,接下來(在篩選器運算符)你剔除不符合的數據。物理上你讀取的記錄數比你邏輯請求的數多不少。在這個例子裏,我從Sales.SalesOrderDetail表讀取了121317條記錄,但沒有1條記錄符合個人查詢謂語(基於ModifiedDate列)。所以全部列在篩選器運算符被剔除。性能
如今假設你在像可重複讀(Repeatable Read)這樣更多限制的事務隔離級別運行這個查詢。在這個狀況下,SQL Server必須把持共享鎖直到事務結束。也就是說,當你讀取超過5000條記錄時,在彙集索引掃描期間,SQL Server會觸發鎖升級。這會很糟糕,由於過後讀取的不符合的數據會在篩選器剔除。你啥也沒作就觸發了鎖升級。優化
你必須接受篩選器運算符很是很差。但咱們能夠作的更好麼?固然,由於SQL Server也支持所謂的剩餘謂語(Residual Predicate):當記錄從數據頁讀取時,這個謂語會「直接」在存儲引擎內部評估。當謂語知足時,行是在存儲引擎外傳入執行計劃。若是謂語不知足,行會忽略並消失。在執行計劃裏,它不會移交給下個運算符。spa
這個和篩選器運算符徹底不一樣!使用剩餘謂語,你只處理在執行計劃裏邏輯請求的行(你仍是要讀取請求的數據頁……)。當基於剩餘謂語沒有符合的行時,沒有行在執行計劃裏運輸。咱們來看下面的查詢。scala
1 SELECT * FROM Person.Person WITH (INDEX(1)) 2 WHERE ModifiedDate > '20200501' 3 GO
這裏我從Person.Person表請求行,一樣對於選擇的查詢謂語沒有行返回。但此次SQL Server能把查詢謂語以剩餘謂語的方式壓入存儲引擎。所以查詢謂語在存儲引擎裏直接評估:code
當你在可重複讀(Repeatable Read)事務隔離級別裏運行這個查詢時,你不會再觸發鎖升級,由於在執行計劃裏你不處理任何行——在存儲引擎裏它們就被或略了。所以對於查詢,你是否會觸發鎖升級取決與你的執行計劃狀況……
如你在這篇文章裏所見,執行計劃狀況會大大影響SQL Server的鎖行爲。剩餘謂語就像帥選器運算符,但它是在存儲引擎裏,從數據頁讀取行後,直接評估。它是SQL Server僱傭的性能優化者。
在一些狀況下,查詢優化器能夠把查詢謂語做爲剩餘謂語壓入存儲引擎,但在一些狀況下作不到,查詢優化器只能引入篩選器運算符。不要問我什麼狀況是能夠的,什麼狀況是不能夠的。這個行爲微軟也沒有文檔說明……
感謝關注!
http://www.sqlpassion.at/archive/2016/02/29/lock-escalations-and-execution-plan-shapes/