2009-10-08 119 views
4

當通過DataReader從SQL服務器查詢返回大型數據集時,我們的數據層存在一些問題。當我們使用DataReader填充業務對象並將它們序列化回客戶端時,獲取可能需要幾分鐘的時間(我們向用戶展示了進展:-)),但是我們發現有一些相當硬核鎖定正在進行在受影響的表上導致其他更新被阻止。使用SQL Server鎖定的DataReader行爲

所以我想我的天真的問題是,在什麼時候執行查詢實際放棄的結果取出鎖?我們似乎發現,鎖直到DataReader的最後一行被處理,DataReader實際上關閉 - 這看起來是否正確?關於DataReader如何在幕後工作的快速101會很好,因爲我一直在努力尋找任何體面的信息。

我應該說我意識到鎖定問題是主要關心的問題,但我只關心DataReader這裏的行爲。

回答

11
  1. 執行查詢期間如果網絡緩衝區已滿,SQL Server可以掛起查詢。如果客戶端沒有跟上網絡,就會發生這種情況。它不調用SqlDataReader.Read()。當網絡緩衝區被釋放時,即恢復SQL Server查詢。當客戶端恢復SqlDataReader.Read()時。這意味着,當您從數據讀取器讀取數據集結果時,查詢仍在服務器上執行。還有更多的細節,比如網絡緩衝區的大小,客戶端使用SqlBytes.Stream等的BLOB操作,但其主旨是慢速客戶端可能導致查詢被掛起,查詢在客戶端結束時結束。

  2. 在正常隔離級別(讀取提交)下讀取數據時,SQL Server將在其讀取的行上放置短期共享鎖。在更高的隔離級別下,鎖被長期存在,直到交易結束。

  3. 如果沒有使用事務,則每個SELECT語句將在語句的持續時間內創建一個隱式只讀轉換。

因此,從1,2,3,我們可以看到一個緩慢的客戶端運行在高隔離級別的查詢會導致共享鎖將舉行一個很長一段時間。

現在,你需要詳細說一下你觀察:

  • 正在舉行什麼樣的鎖通過此查詢?
    • S,U,X?
    • 行,頁,表?
    • 範圍鎖?
  • 鎖升級是否發生?
  • 爲什麼是在查詢期間持有的鎖?
    • 您使用REPEATABLE READ或SERIALIZATION隔離級別嗎?如果是,爲什麼?
    • 你使用鎖提示嗎?如果是,爲什麼?

您最好的選擇很可能是snapshot isolation(至少需要SQL Server 2005中),無論是作爲快照隔離級別或讀COMMITED快照。這將完全消除鎖定問題,但會在tempdb上創建潛在的一些IO壓力。

其他的解決方案是使用遊標,但對現有的代碼庫很複雜,複雜且仍然容易出錯(必須獲得遊標類型)。

順便說一句,我做不是建議改變客戶端行爲。我假設你現在正在讀取它們時在SqlDataReader.Read循環中編組業務對象,這就是實現它的方法。預先讀入內存然後編組可能會在大型數據集中增加更多問題。

+0

第1點很有趣:我經常看到它 – gbn 2009-10-08 18:50:18

+0

非常棒的答案,謝謝。查詢在讀取提交隔離級別下執行,並將READ COMMITTED SNAPSHOT ON設置確實解決了阻塞問題。我仍然很好奇爲什麼這些鎖(其中大多數是S Page鎖)在查詢期間被保留,因爲沒有使用鎖提示,並且根據SQL Profiler,沒有發生鎖升級。 – Graham 2009-10-09 07:38:33

1

選擇到一個臨時表將減少鎖定時間

選擇等等從TBL到#TEMP召開併發布

SELECT * FROM #TEMP < <採取一切你現在想

時間< <鎖