基數估計差會使 INSERT 從最小日誌記錄中失格嗎?
為什麼第二個
INSERT
語句比第一個慢約 5 倍?從生成的日誌數據量來看,我認為第二個不符合最小日誌記錄的條件。但是,Data Loading Performance Guide中的文件表明兩個插入都應該能夠被最小化記錄。因此,如果最小日誌記錄是關鍵的性能差異,為什麼第二個查詢不符合最小日誌記錄的條件?可以做些什麼來改善這種情況?
查詢 #1:使用 INSERT…WITH (TABLOCK) 插入 5MM 行
考慮以下查詢,它將 5MM 行插入到堆中。此查詢執行
1 second
並生成64MB
由 報告的事務日誌數據sys.dm_tran_database_transactions
。CREATE TABLE dbo.minimalLoggingTest (n INT NOT NULL) GO INSERT INTO dbo.minimalLoggingTest WITH (TABLOCK) (n) SELECT n -- Any table/view/sub-query that correctly estimates that it will generate 5MM rows FROM dbo.fiveMillionNumbers -- Provides greater consistency on my laptop, where other processes are running OPTION (MAXDOP 1) GO
查詢 #2:插入相同的數據,但 SQL 低估了行數
現在考慮這個非常相似的查詢,它對完全相同的數據進行操作,但碰巧是從
SELECT
基數估計太低的表(或在我的實際生產案例中具有許多連接的複雜語句)中提取的。此查詢在事務日誌數據中執行5.5 seconds
並生成461MB
。CREATE TABLE dbo.minimalLoggingTest (n INT NOT NULL) GO INSERT INTO dbo.minimalLoggingTest WITH (TABLOCK) (n) SELECT n -- Any table/view/sub-query that produces 5MM rows but SQL estimates just 1000 rows FROM dbo.fiveMillionNumbersBadEstimate -- Provides greater consistency on my laptop, where other processes are running OPTION (MAXDOP 1) GO
完整腳本
請參閱此 Pastebin以獲取完整的腳本集以生成測試數據並執行這些場景中的任何一個。請注意,您必須使用處於
SIMPLE
恢復模式的數據庫。業務環境
我們半頻繁地移動數百萬行數據,重要的是讓這些操作盡可能高效,無論是在執行時間還是磁碟 I/O 負載方面。我們最初的印像是創建一個堆表並使用
INSERT...WITH (TABLOCK)
是一個很好的方法,但現在我們在實際生產場景中觀察到上面展示的情況(儘管查詢更複雜,而不是此處為簡化版)。
為什麼第二個查詢不符合最小日誌記錄的條件?
最小日誌記錄可用於第二個查詢,但引擎選擇在執行時不使用它。
有一個最小門檻值,
INSERT...SELECT
低於該門檻值它選擇不使用批量載入優化。設置批量行集操作涉及成本,並且僅批量插入幾行不會導致有效的空間利用。可以做些什麼來改善這種情況?
SELECT INTO
使用沒有此門檻值的許多其他方法之一(例如)。或者,您可能能夠以某種方式重寫源查詢,以將估計的行數/頁數提高到超過INSERT...SELECT
.有關更多有用資訊,另請參閱Geoff 的自我回答。
可能有趣的瑣事: 僅在不使用批量載入優化時
SET STATISTICS IO
報告目標表的邏輯讀取。