1. 程式人生 > >Sql Server優化---統計信息維護策略

Sql Server優化---統計信息維護策略

都是 情況 數據流 with 3.2 可靠 date uil 解釋


首先解釋一個概念,統計信息是什麽:
  簡單說就是對某些字段數據分布的一種描述,讓SQL Server大概知道預期的數據大小,從而指導生成合理執行計劃的一種數據庫對象

默認情況下統計信息的更新策略:
  1,表數據從0行變為1行
  2,少於500行的表增加500行或者更多
  3,當表中行多於500行時,數據的變化量大於500+20%*表中數據行數

非默認情況下,促使已有統計信息更新的因素(包括但不限於下面三種,別的我也沒想起來):
  1,rebulid\Reorg index
  2,主動update statistics
  3,數據庫級別的sp_updatestats

開始問題:

對於大表的更新策略是:數據的變化量大於500+20%*表中數據行數
比如對於1000W數據量的表,數據變化要超過500+1000W*20%=2,000,500之後才能觸發統計信息的更新,
這一點大多數情況下是無法接受的,為什麽?因為該規則下觸發統計信息更新的閾值太大,會導致某些統計信息長期無法更新,
由於統計信息導致的執行計劃不合理的情況已經在實際業務中屢見不鮮,對於統計信息的更新已經顯得非常必要

同時,僅僅靠sqlserver自己更新統計信息,也不一定可靠,因為統計信息中還要一個取樣行數的問題,這個也非常重要
因為SQL Server默認的取樣行數是有上限的(默認取樣,未指定取樣百分比或者SQL Server自動更新統計信息時候的取樣百分比),
這個上限值在100W行左右(當然也不肯定,只是觀察),對於超過千萬行的表,這個取樣比例還是非常低的
比如下圖超過3億行的表,更新統計信息時候未指定取樣百分比,默認取樣才去了84萬行)
據樓主的觀察看,對於小表,不超過500W行的表,默認的取樣比例是沒有問題的,對於較大的表,比如超過500W行的表(當然這個500W行也是一個參考值,不是絕對值)
因此說默認取樣比例是根本無法準確描述數據分布的。

技術分享

由此看來,人工介入統計信息的更新是非常有必要的。那麽如何更新索引的統計信息,有沒有一種固定的方式?答案是否定的。

首先來看能夠觸發統計信息更新的方式

1,Rebulid\Reorg index
  當然Rebulid\Reorg索引只是附帶更新了索引的統計信息,主要是為了整理了索引碎片,
  對於大表,代價相當大,數據庫的維護策略,沒有一概而論的方法,
  對於較小的數據庫或者是較小的表,比如幾十萬幾百萬的表,每天一個rebuild index都可以,
  但是這種經驗移植到大一點的數據庫上恐怕就不好使了(正如名人的成功經驗不可復印一樣,每個人生活的環境不一樣,不能一概而論)。
  這種Rebulid\Reorg index對資源的消耗以及時間代價上都會相當大,甚至有些情況下是不會給你機會這麽做的。
  比如下面rebuild一個復合索引的耗時情況,僅僅是一個表上的一個索引,就花費了5分鐘的時間
  一個業務復雜的表上有類似這麽三五個索引也是正常的,
  照這麽算下去,如果全庫或者是整個實例下的十幾個庫,每個庫數百張表全部這麽做,要多長時間,代價可想而知
  說不定整都沒整完,維護窗口期的時間就到了,除非數據庫不大(究竟大小的臨界值為多少?個人覺得可以粗略地認為100GB吧),否則是不可以這麽做的。

  因此可以認為:通過重建或者重組索引來更新索引統計信息,代價太大了,基本上是不現實的。

  技術分享


2,update statistics

  正是我想重點說的,因為我這裏不具體說語法了,具體語法就不做詳細說明了,
  簡單來說,大概有如下幾種選擇:
  一種默認方式,另外還可以是全表掃描的方式更新,還有就是是指定一個取樣百分比,如下:

--默認方式更新表上的所有統計信息
update statistics TableName
--對指定的統計信息,采用全表掃描的方式取樣
update statistics TableName(index_or_statistics__name) with FullScan 
--對指定的統計信息,采用指定取樣百分比的方式取樣
update statistics TableName(index_or_statistics__name1,index_or_statistics__name2) with sample 70 percent

  相對於重建或者重組索引,update statistics 也是通過掃描數據頁(索引頁)的方式來獲取數據分布,但是不會移動數據(索引)頁,
  這是Update Statistics代價相對於Rebuild索引小的地方(即便是Update Statistics的時候100%取樣)
  關鍵在於第三種方式:人為指定取樣百分比,如果取樣百分比為100,那跟FullScan一樣
  如果不用100,比如80,60,50,30,又如何選擇?取樣百分比越高,得到的統計信息越準確,但是代價越大,取樣越小效率越高,但是誤差的可能性會變大,怎麽辦,這就需要找一個平衡點。
  那麽究竟要取樣多少,既能在更新統計信息的效率上可以接受,又能夠使得統計信息達到相對準確地描述數據分布的目的,
  這是還是一個需要慎重選擇的問題,為什麽?參考:http://www.cnblogs.com/wy123/p/5875237.html
  如果統計信息取樣百分比過低,會影響到統計信息的準確性,
  如果過於暴力,比如fullscan的方式掃描,
  參考下圖,一個表就Update了50分鐘(當然這是一個大表,上面有多個索引統計信息以及非索引統計信息)。如果有數十張類似的表,效率可想而知
  總之就是,沒有一個固定的方式,數據庫不大,怎麽做問題都不大,數據庫一大,加上維護的窗口期時間有限,要在統計信息的質量和維護效率上綜合考慮

  技術分享

3,數據庫級別的sp_updatestats

  用法:
  exec sp_updatestats
  或者
  exec sp_updatestats @resample = ‘resample‘

  指定 sp_updatestats 使用 UPDATE STATISTICS 語句的 RESAMPLE 選項。

  對於基於默認抽樣的查詢計劃並非最佳的特殊情況,SAMPLE 非常有用。
  在大多數情況下,不必指定 SAMPLE,
  這是因為在默認情況下,查詢優化器根據需要采用抽樣,並以統計方式確定大量樣本的大小,以便創建高質量的查詢計劃。

  如果未指定 ‘resample‘,則 sp_updatestats 將使用默認的抽樣來更新統計信息。
  默認值為 NO。

  直接執行exec sp_updatestats更新統計信息,取樣密度是默認的,
  究竟這默認值是多少,MSDN上說默認情況下是“查詢優化器根據需要采用抽樣”,我想著采樣算法應該沒那麽簡單粗暴
  目前也不知道具體是怎麽一個算法或者采樣方式,如果有知道園友的話請不惜賜教,謝謝

4,TraceFlag 2371

開啟TraceFlag 2371之後,統計信息的變化是根據表做動態變化的,
打破了觸發大表統計信息更新的當表中行多於500行時,數據的變化量大於500+20%*表中數據行數 閾值
參考:https://blogs.msdn.microsoft.com/saponsqlserver/2011/09/07/changes-to-automatic-update-statistics-in-sql-server-traceflag-2371/

  在下圖中,你可以看到新公式的工作方式,對於小表,閾值仍舊是在20%左右,
  只有超過25000行之後,此動態規則才會被觸發生效
  隨著表中數據行數的增加,(觸發統計信息變更)的百分比會變的越來越低,
  比如,對於100,00行的表,觸發統計信息更新的閾值已經降低為10%,
  對於1,000,000行的表,觸發統計信息更新的閾值已經降低為3.2%。

  技術分享

  對於10,000,000或者是50,000,000行的表,觸發統計信息更新的閾值為少於1%或者0.5%,
  而對於他100,000,000行的表,僅僅要求變化在0.31%左右,就可以出發統計信息的更新。

  但是個人認為,這種方式也不一定靠譜,雖然開啟TraceFlag 2371之後觸發更新索引統計信息的閾值降低了,但是取樣百分比還是一個問題,
  之前我自己就有一個誤區,看統計信息的時候只關註統計信息的更新時間(跟自己之前遇到的數據庫或者表太小有關)
  對於統計信息,及時更新了(更新時間比較新)不等於這個統計信息是準確的,一定要看取樣的行數所占總行數的百分比

如何有效維護索引統計信息?

  上面說了,要使獲取相對準確的統計信息,就要在更新統計信息時候的取樣百分比,
  對於小表,即便按照其默認的變化閾值觸發統計信息更新,或者是按照100%取樣更新統計信息,都是沒有問題,
  對於大表,一定要考慮在其達到默認觸發統計信息更新的閾值之前人為更新這個統計信息,但是大表的100%取樣統計是不太現實的(性能考慮)
  取樣百分比越高,得到的統計信息越準確,但是代價越大,這就需要找一個平衡點,那麽如果更新大表上的統計信息呢?
  如果是認為幹預統計信息的生成,就要考慮兩個因素:一是數據變化了多少之後更新?二是更新的時候,以什麽樣的取樣來更新?
  我們知道,一個表的數據變化信息(增刪改)記錄在sys.sysindexes這個系統表的rowmodctr字段中,
  該表的統計信息更新之後,該字段清零,然後再次累積記錄表上的數據變化。

  技術分享

  這個信息非常好使,為人工更新統計信息提供了重要的依據,
  比如,對於1000W行的表,可以指定變化超過20W行(根據業務情況自定義)之後,手動更新統計信息,
  對於5000W行的表,可以指定變化超過60W行(根據業務情況自定義)之後,手動更新統計信息,
  同時根據不同的表,在相對較小的表上,指定相對較高的取樣百分比,在相對較大的表上,指定相對較低的取樣百分比
  比如對於1000W行的表,更新統計信息的時候取樣百分比定位60%,對於5000W行的表,更新統計信息的時候取樣百分比定位30%
  這樣,可以自行決定數據變化了多少之後更新統計信息,以及動態地決定不同表的不同取樣百分比,達到一個合理的目的。
  當然,最後強調一下,我說的每一個數據都是相對的,而不是絕對的,都是僅做參考,
  具體還要你自己結合自己的服務器軟硬件以環境及維護窗口時間去嘗試,一切沒有死的標準。

總結:統計信息的準確性對執行計劃的生成有著至關重要的影響,本文粗略分析了統計信息的跟新規律以及要更新統計信息時候要註意的問題,
   在人為幹預統計信息更新的時候,需要根據具體的情況(表數據流量,服務器軟硬件環境,維護窗口期等)在效率與準確性之間作出合理的選擇。

Sql Server優化---統計信息維護策略