增量統計:找出哪些分區已被重新採樣
**TL;DR:**當使用增量統計時,是否有可能找出哪些分區已被重新採樣而哪些沒有?平台為 SQL Server 2014 企業版。
帶有一些背景資訊的長版本是這樣的。
假設一個相當典型的 DW 環境,有一個分區表。分區基於日期列。這是通過將暫存數據載入到單獨的表上完成的,並且在預處理之後,使用分區切換將數據移動到生產事實表中。哦,一個聚集列儲存索引正在使用中。使用了大約一千個分區。數據庫在虛擬機上執行。
事實表中有大約 7.5 gigarows (100 GB)。每日增長約為 5 兆行。這是觸發自動統計更新的增長率太小,保存跟踪標誌 2371(尚未嘗試過)。
開發人員對過時統計數據的下意識反應是更新它們。對於 7.5 gigarows,所有統計數據的完整更新需要大約五個小時。對於單個統計資訊更新,處理性能約為 20 分鐘或每秒 90 兆行。
由於系統位於 VM 平台上,因此業務規則限制了其成本。記憶體和 IOPS 都不容易增加。五小時的更新工作太慢了,無法包含在每晚的 ETL 流程中,因此要麼統計資訊過時,要麼在意外時間更新,要麼將在維護視窗中更新。
由於 SQL Server 是 2014 企業版,它支持增量統計,聽起來就像解決方案。將統計資訊轉換為增量統計資訊後,處理單個分區的單個統計資訊僅需 20 秒。新切換的分區總計約五分鐘。這聽起來很棒,當然也適合 ETL 過程。
我想知道的是如何在分區切換環境中管理增量統計資訊。假設統計數據在日期 D 被轉換和更新為增量,那麼如何在日期 D+2 上找出未處理的分區?在 ETL 過程中更新統計資訊是微不足道的,因為切換過程顯然知道分區 ID。但是,如果存在未重新採樣的分區,如何找到這些分區?
- 統計數據的最後更新可以從
sys.dm_db_stats_properties
- 分區號可從
sys.partitions
- 分區函式值在
sys.partition_range_values
可以選擇統計數據的最後更新日期 L 並將其與今天的日期 T 進行比較。然後計算 L 指向的分區 id 以及它是否與 T 相同。然後繼續更新所有分區 ID [L, T)。這聽起來很棘手且容易出錯,那麼有更好的方法嗎?顯示哪些分區用於重採樣的 DMV 會很好,但沒有一個,是嗎?
我的第一個問題是首先問為什麼你實際上使用增量。這是我發布的關於增量統計的答案,Erin Stellato 的部落格文章闡明了增量統計的主要抱怨和陷阱之一(優化器未在分區級別使用它們),以及我的兩篇 部落格文章通過評估增量統計的任何潛在案例來工作。
話雖如此,要知道何時對分區的統計資訊進行了採樣,您可以使用未記錄的 DMF (
sys.dm_db_stats_properties_internal()
) 來獲取分區級別的資訊。我對這篇部落格文章發表了評論,該文章描述瞭如何在相當高的層次上理解層次結構。select sysdatetime(), schema_name = sh.name, table_name = t.name, stat_name = s.name, index_name = i.name, leading_column = index_col(quotename(sh.name)+'.'+quotename(t.name),s.stats_id,1), s.stats_id, parition_number = isnull(sp.partition_number,1), s.has_filter, s.is_incremental, s.auto_created, sp.last_updated, sp.rows, sp.rows_sampled, sp.unfiltered_rows, modification_counter = coalesce(sp.modification_counter, n1.modification_counter) from sys.stats s join sys.tables t on s.object_id = t.object_id join sys.schemas sh on t.schema_id = sh.schema_id left join sys.indexes i on s.object_id = i.object_id and s.name = i.name cross apply sys.dm_db_stats_properties_internal(s.object_id, s.stats_id) sp outer apply sys.dm_db_stats_properties_internal(s.object_id, s.stats_id) n1 where n1.node_id = 1 and ( (is_incremental = 0) or (is_incremental = 1 and sp.partition_number is not null) ) and t.name = '<<TABLENAME>>' and s.name like '<<STATNAME>>%' order by s.stats_id,isnull(sp.partition_number,1);