2010-06-02 70 views
14

比方說,您有一個包含大量文本數據的大約500萬條記錄和nvarchar(max)列的表格。如果SomeOtherColumn = 1以最快的方式設置,您希望將此列設置爲NULL大規模更新的最快方法

蠻力UPDATE在這裏工作不好,因爲它會創建大型隱式事務,並且永遠持續下去。

在小批量的50K記錄中一次更新可行,但在強壯的32核心/ 64GB服務器上仍需要47小時才能完成更新。

有什麼辦法可以更快地完成此更新?是否有任何神奇的查詢提示/表格選項犧牲了別的東西(比如併發)來換取速度?

注意:創建臨時表或臨時列不是一個選項,因爲此nvarchar(max)列涉及大量數據,因此會消耗大量空間!

PS:是的,SomeOtherColumn已經編入索引。

+0

另請參閱:http://stackoverflow.com/questions/571750/make-sql-server-faster-at-manipulating-data-turn-off-transaction-logging – 2010-06-02 03:22:28

+0

你是如何做'50K批記錄在一次更新?它是否與存儲過程?如果是這樣,你可以把代碼? – Fede 2010-06-02 03:30:10

+0

@ user356004:在重新閱讀時,我不禁想到服務器負載很重或者設置不正確:那些時間看起來非常高。 – 2010-06-02 04:07:59

回答

1

您是否嘗試過在someOtherColumn上放置索引或統計信息?

+0

如果性能問題是因爲沒有索引,並且需要進行表掃描來識別要更新的行,那麼創建新索引併發布更新需要的時間長(或更長)? – 2010-06-02 03:18:57

+0

真的是nvarchar(max)列的索引嗎? – Paparazzi 2017-01-08 19:41:30

3

您可以將數據庫恢復模式設置爲Simple以減少日誌記錄,但是如果不考慮生產環境的全部影響,則不這樣做。

表上有什麼索引?鑑於批量更新約。 50,000行需要這麼長時間,我會說你需要一個索引。

0

嘗試索引'SomeOtherColumn'... 50K記錄應該快速更新。如果已經有索引,看看索引是否需要重新組織,並且已經收集了統計數據。

0

如果您正在運行的生產環境沒有足夠的空間來複制所有表格,我相信您遲早會在尋找麻煩。

如果你提供了有關SomeOtherColumn = 1的行數的一些信息,或許我們可以想到另一種方式,但我建議:

0)備份你的表 1)指數的標誌欄 2)套裝將表選項設置爲「無日誌傳輸」...如果可能的話 3)編寫存儲過程以運行更新

+0

順便說一句...你需要在生活中多次運行這個程序嗎? – 2010-06-02 03:24:04

+1

如何將表選項設置爲「無日誌傳輸」? – user356004 2010-06-07 09:56:36

3

希望您已經將您設置爲空的列上的所有索引都刪除,包括全文索引。如前所述,暫時關閉事務和日誌文件就可以解決問題。備份你的數據通常也會截斷你的日誌文件。

+0

絕對要確保你放棄索引。過去我已經爲我縮短了很多事情。 – 2010-06-02 04:09:25

1

這真的幫了我。我用這個從2小時到20分鐘。

/* I'm using database recovery mode to Simple */ 
/* Update table statistics */ 

set transaction isolation level read uncommitted  

/* Your 50k update, just to have a measures of the time it will take */ 

set transaction isolation level READ COMMITTED 

根據我的經驗,在2005年MSSQL工作,移動每天(自動)從一臺400萬46字節記錄(不爲nvarchar(最大)雖然)數據庫中的其他表在不同的數據庫中提取在QuadCore 8GB,2Ghz服務器中約20分鐘,並且不會影響應用程序性能。通過移動我的意思是插入選擇,然後刪除。即使刪除的表有28M記錄,並且它每分鐘不停地產生4K插入但沒有更新,CPU使用率也不會超過30%。那麼,這是我的情況,它可能會有所不同,這取決於您的服務器負載。

未提交讀

「指定的語句(您的更新),可以讀取已被其他事務修改但尚未提交的行。」就我而言,這些記錄是隻讀的。

我不知道什麼rg-tsql的意思,但here你會發現有關MSSQL中事務隔離級別的信息。

+1

「rg」是RedGate,一家贊助公司,他們在[tsql]標籤的結果上做廣告。 – Corey 2010-06-02 04:09:43

+1

請務必小心,並確保您瞭解閱讀未登記交易的含義。是的,您的流程在刪除條目之前不必等待打開的事務提交,但當然如果事務沒有提交,所有這些都意味着您錯誤地刪除了該行! – Cobusve 2010-06-07 10:43:45

7

從我可以看到的一切看起來並不像你的問題與索引有關。

關鍵似乎在於你的nvarchar(max)字段包含「大量」數據。考慮SQL執行此更新所需執行的操作。

由於您正在更新的列可能超過8000個字符,因此將其存儲在頁外,這意味着當該列不爲NULL時,需要額外努力讀取此列。

當您運行一批50000更新時,SQL必須將其置於隱式事務中,以便在出現任何問題時能夠回滾。爲了回滾它必須將該列的原始值存儲在事務日誌中。

假設(爲了簡單起見)每列平均包含10,000個字節的數據,這意味着50,000行將包含大約500MB的數據,這些數據必須臨時存儲(以簡單恢復模式)或永久存儲(完全恢復模式)。

無法禁用日誌,因爲它會危及數據庫的完整性。

我在我的狗慢桌面上運行了一個快速測試,運行的批量甚至10,000變得非常慢,但將大小降低到1000行,這意味着臨時日誌大小約爲10MB,工作得很好。

我加載了一個包含350,000行的表格,並標記了50,000個用於更新的表格。這項工作大約在4分鐘內完成,而且由於它線性擴展,因此我應該可以在我的1處理器2GB桌面上大約6小時內在我的狗慢桌面上更新整個5百萬行,因此我期望在您的強大服務器上支持更好通過SAN或其他東西。

您可能希望將更新語句作爲select運行,只選擇主鍵和大型nvarchar列,並確保它按預期運行。

當然,其他用戶可能會鎖定其他用戶鎖定服務器上的存儲或內存中的內容或爭用,但由於您沒有提及其他用戶,因此我將假設您擁有單用戶模式下的DB。

作爲一種優化,您應確保事務日誌位於與數據不同的物理磁盤/磁盤組上,以最大限度地縮短尋道時間。