天天看點

SQL Server 利用鎖提示優化Row_number()-程式員需知

網站中一些老頁面仍采用Row_number類似的開窗函數進行分頁處理,此時如果遭遇挖墳帖的情形可能就需要漫長的等待且消耗巨大.這裡給大家介紹根據Row_number()特性采用特定鎖Hint提升查詢速度.

  直接上菜

  腳本環境可在SQL Server優化技巧之SQL Server中的"MapReduce"找到

  如下查詢在分頁中比較常見

set statistics time on

 select * from 
(
select ProductID, rn = ROW_NUMBER() OVER (ORDER BY ProductID)
from [bigTransactionHistory]
) as t
where t.rn between 15631801 and 15631802      

這條查詢在我的電腦上執行了15S,這還是資料全在記憶體中的情形!如圖1-1

                                                                圖1-1

一個簡單的執行計劃執行如此之長有點匪夷所思,畢竟邏輯讀才6W多,且無實體讀

,而且CPU時間與占用時間相差無幾,排除了阻塞之類的因素後我們把消耗定位在這個查詢本身上.這時提一個Row_number()的特點,它可在萬千資料中将其序列化讓我們找到我們想要的精确資料點,但就此預設的實作方式上是為每一行資料都加一個行鎖.

我們開啟Trace Flag 1200再次執行語句捕捉下執行時的鎖.可以看到Row_number()在實作上未進行鎖更新如圖1-2

Code

SQL Server 利用鎖提示優化Row_number()-程式員需知
SQL Server 利用鎖提示優化Row_number()-程式員需知
dbcc traceon(3604,1200,-1)

select * from 
(
select ProductID, rn = ROW_NUMBER() OVER (ORDER BY ProductID)
from [bigTransactionHistory]
) as t
where t.rn between 15631801 and 15631802      

View Code

                                                   圖1-2

到此我們對此問題的解決方式也就出來了:可采用鎖hint的形式手動為其更新

這裡我采用頁鎖,如圖1-3

而兩者從執行計劃上看是相同的,預估也完全一樣如圖1-4

SQL Server 利用鎖提示優化Row_number()-程式員需知
SQL Server 利用鎖提示優化Row_number()-程式員需知
select * from 
(
select ProductID, rn = ROW_NUMBER() OVER (ORDER BY ProductID)
from [bigTransactionHistory] with(paglock)
) as t
where t.rn between 15631801 and 15631802      

                                         圖1-3

                                                             圖1-4

可以看到我們通常的檢視執行計劃的方式在此就不太适合了,需要我們對資源消耗有更詳細的認知.

注:平時我們還可用Trace Profiler捕捉鎖,但需注意慎用.

   Row_number()預設看不到鎖更新,全局性能瓶頸下可能回更新

   如果你的應用不在乎髒讀,nolock方式更愉快:)

其它:當資料被更新發生阻塞時,有時業務同僚會問到底更新了哪條資料有木有?

這裡寫了個簡單的查詢以便找到具體更新被鎖住的行,如圖2-1

begin tran ttt
update dbo.[bigProduct] set size=111 where ProductID<1100
-- rollback when finish test
--rollback tran ttt

--open another session

SELECT * FROM [bigProduct] with(nolock)
WHERE
    %%LOCKRES%% IN
    (
        SELECT 
            tl.resource_description
        FROM sys.dm_tran_locks AS tl
        INNER JOIN sys.partitions AS t2 ON
            t2.hobt_id = tl.resource_associated_entity_id
        WHERE 
            t2.object_id = OBJECT_ID('bigProduct')
            AND tl.resource_type = 'KEY'
    )      

                                                          圖2-1

結語:系統内任何元素都有可能成為影響平衡的絆腳石.找到它,了解它,利用它.

認為有收獲的同學請點贊.

Involuntary DBA

繼續閱讀