SQL Server 執行計劃利用統計信息對數據行的預估原理以及SQL Server 2014中預估策略的改變

来源:http://www.cnblogs.com/wy123/archive/2016/08/22/5790855.html
-Advertisement-
Play Games

前提 本文僅討論SQL Server查詢時, 對於非複合統計信息,也即每個欄位的統計信息只包含當前列的數據分佈的情況下, 在用多個欄位進行組合查詢的時候,如何根據統計信息去預估行數的。 利用不同欄位的統計信息做數據行數預估的演算法原理,以及SQL Server 2012和SQL Server 2014 ...


 

前提

      本文僅討論SQL Server查詢時,
    對於非複合統計信息,也即每個欄位的統計信息只包含當前列的數據分佈的情況下,
    在用多個欄位進行組合查詢的時候,如何根據統計信息去預估行數的。
    利用不同欄位的統計信息做數據行數預估的演算法原理,以及SQL Server 2012和SQL Server 2014該演算法的差異情況,
    這裡暫時不涉及複合統計信息,暫不涉及統計信息的更新策略及優化相關話題,以及其他SQL Server版本計算方式。
  

 

統計信息是什麼

    簡單說就是對某些欄位的數據分佈的一種描述,讓SQL Server在根據條件做查詢的時候,大概知道預期的數據大小,
    從而指導生成合理執行計劃的一種資料庫對象

 

統計信息的分類

     索引上會自動創建統計信息,SQL Server也會根據具體的查詢,在某些非索引自動創建索引,當然也可以通過手動方式創建統計信息。
     先來直觀地瞭解一下統計信息長什麼樣,參考截圖,就是這麼個樣子,
     _WA_Sys_****開頭的是系統根據需要創建的統計信息,
    與索引同名的是索引上創建的統計信息,
    手動創建統計信息也可以在滿足SQL Server命名要求的情況下自行命名。

  

  下麵一個是索引的統計信息。

    

 

統計信息的作用

    查詢引擎根據統計信息提供的數據做出合理的執行計劃。
    那麼,查詢引擎究竟是怎麼利用統計信息做預估的呢,
    以及下麵將要提到的SQL Server 2014中較之前的版本有哪些變化?
    本文將對此兩點做一個簡單的分析來說明SQL Server是怎麼根據統計信息做估算的,下麵開始正文。

 

    測試環境搭建

  習慣性地做一個演示的環境,創建一個表,寫入100W的數據後面測試用。

create table TestStatistics 
(
    Id int identity(1,1),
    Status1 int,
    Status2 int,
    Status3 int
)
insert into TestStatistics values (RAND()*1000,RAND()*250,RAND()*50) go 1000000

表中有四個欄位,第一個是自增列,主要看Status1,Status2,Status3這三個欄位,
三個欄位的取值都是用隨機數乘以一個常量繫數的出來的,
因此這三個欄位的數據分佈範圍分別是
Status1:0-999(1000種數據分佈)
Status2:0-249(250種數據分佈)
Status3:0-49(50種數據分佈)
這個後面有用。

 

 

 

首先在SQL Server 2012中做測試

      先做這麼一個查詢:select * from TestStatistics where Status1=885 and Status2=88 and Status3=8
    這個查詢完成之後,表上自動創建一個三個統計信息,
    這三個統計信息分別是Status1,Status2,Status3這個三個欄位的數據分佈描述

  

  

 

      首先來看一下其中這個_WA_Sys_00000002_0EA330E9,也即Status1這個列的統計信息的詳細信息,
    註意All density欄位值,選擇性是反應一個表中該欄位的重覆數據有多少或者說唯一性有多少,
    計算方法是:1/表中該欄位非重覆個數。

  

    上面說了,這個Status1這個列的取值範圍是0-999,一共有1000中取值可能行,
    那麼這個選擇行就是1/1000=0.001,所以也是吻合這裡的All density=0.001的

  

  

  照這麼計算,其餘兩個欄位的選擇度分別是1/250=0.004 和1/50=0.02,分別如下截圖的 All density。

         

  

 

 

  執行計劃對數據行的預估

  

  說完統計信息的基礎問題之後,我們就可以來觀察執行計劃對目標數據的預估規律了。
  我們來看這麼一個查詢,如下,註意這個是查詢的條件是參數變數,而不是直接的值,後面我會解釋為什麼這麼做。
  來觀察執行計劃對數據行的預估:可以看出來,預估為4行。

  

  

  那麼這個4行是怎麼計算出來的呢?

  這就要利用到我們上面的選擇性了,
  Status1欄位的選擇性是0.001,Status2的選擇性是0.04,
  在SQL Server 2012中,對數據行的預估計算方式是各個欄位的選擇性的乘積,
  假如Pn代表不同欄位的選擇性,那麼預估行數的計算方法就是: 預估行數=p0*p1*p2*p3……*RowCount
  因此,執行計劃顯示的:預估行數=0.001*0.004*總行數(也即1000000)= 4
 

 

說到這裡解釋兩個可能存在的幾個疑問:

  第一,上述示例是用兩個欄位查詢的,為什麼不拿三個欄位做演示說明? 

 首選,不管是多少個欄位查詢,預估行數符合上述計算方式是沒有問題的,
 但是如果通過上述公式計算出來的結果非常小,在少於1的情況下,SQL Server顯示預估為1行。
 按照上述計算方法,用三個欄位做查詢,
 預估行數=0.001*0.004*0.02*總行數(也即1000000)= 0.08<1,所以預估為1行。

     

 

  第二,為什麼不直接用值查詢,而是用變數做查詢?

       熟悉SQL Server的同學應該都知道,直接用變數查詢的時候,SQL Server編譯的時候不知道具體的參數值,
       在不知道具體參數值的情況下,它是使用欄位的選擇性的時候是用到一般性(或者說是平均)的值,
       也就是統計信息中整體計算出來欄位的選擇性,也即All density=0.001
       這裡暫定認為數據分佈是均勻的,也即每個值分佈差別不大。
       但事實上每個值的分佈的差別還有存在的,
       尤其是分佈不均勻的時候,當然這個是另外一個非常大的話題了,這裡暫不討論。

    


       如果直接用明確的值做查詢。
         比如 select * from TestStatistic where Status1=885 and Status2=88
         SQL Server會根據統計信息中每個欄位 :Status1=885 的行數和 Status2=88行數的具體的值,
         利用上述公式做預估
         那麼就繼續用具體的值做演示說明,
         可以直接用where Status1=885 and Status2=88這個條件查詢來觀察預估結果。

     首先我們看統計信息中Status1=885 的分佈行數,1079行

     

    

     然後再看統計信息中Status2=88 的分佈行數,3996行

     

    

     利用上述公式,預估行數為4.31168行

     

  

      那麼直接利用值做查詢是不是這個預估的行數呢?直接上圖,完美地吻合了上述的計算方法得到的結果。

     

     

    第三,沒有索引的情況下是符合預估的計算方法,如果創建了索引呢?

       查詢條件中的各個列的統計信息是非相關的,
       如果分別在各個列上創建單個列的索引信息,在查詢的時候也屬於非相關統計信息。
       如截圖,也就是說,雖然創建了索引,執行計劃發生了變化,
       從一開始的表掃描變成了通過兩個索引查找後做hash join,然後Loop join查詢數據,咱不管它就是變成什麼執行計划了

        

       但是統對數據的預估還是跟上面全表掃描一樣的,都是預估為4.31168,沒有因為創建了索引以及執行計劃發生了變化而改變(預估行數)。

       因為即便是創建了單列上的索引,執行計劃變了,但是統計信息還是非相關的,也就是一個統計信息只描述一列欄位的分佈情況。

        

 

   

然後在SQL Server 2014中做測試

      上述同樣的數據,我這裡通過link server 將上述SQL Server 2012實例下的測試表的結果導入到SQL Server 2014的實例下的表中。
      現在表結構和數據完全一致。

  

    首選,做一個同樣的測試,利用兩個變數查詢的查詢條件做查詢,看看SQL Server 2014預估的演算法有什麼變化。

    

 

    還記得上面在 SQL Server 2012中同樣的寫法,同樣的數據的預估的情況吧,剛纔預估的是4行,現在怎麼變成63.2456行了?
    預估行數的計算公式變了嗎,當然變了,這正是本文要說的重點。
    那麼SQL Server 2014中是怎麼預估的呢?公式是這麼來的:預估行數 = P0*P11/2  * P21/4 * P31/8……* RowCount 
      那麼來根據此計算方式來計算預估行數的問題:預估行數=0.001*0.0041/2*1000000 = ?
    這裡我就不做開方運算了,拿來主義,直接用SQL Server來算拉倒了,SQL Server給我們提供了一個開方函數(SQRT),真JB好用。

    計算一下結果吧,

    

       沒錯,是63.24555,保留四位有效數字的話就是63.2456了,預估行數跟上面計算出來的結果也是完全吻合的。

 

 

  補充測試1:

     同樣地,用三個條件做查詢,預估演算法也同樣複合上述公式的結果。

    

      按照公式來計算預估行數,選擇性按照整體計算出來的選擇性來,同樣也是吻合的。

    

 

   補充測試2:

      如果把查詢條件換做具體的值,跟在SQL Server 2012中一樣,SQL Server2014 也同樣會根據具體的值得數據做計算
    進行這麼個查詢:select * from TestStatistics2014 where Status1=858 and Status2=88 
    解釋一下為什麼這次Status1換成858了:
    因為即便表結構,數據完全一致吧,受限於統計信息的步長(Steps)只有200,兩個庫的統計信息也不完全一致,統計信息不能精確到任何一個值,
    我們這裡為了演示這個演算法,找一個具體的RANGE_HI_KEY值,比較容易說明問題。

 

      首先看Status1=858的數據分佈情況

    

      再看Status2=88的數據分佈情況

    

    

    利用上述計算方法計算出來的預估:63.27713

    

 

    執行計劃的預估:63.27713,也是完全吻合的。

    

 

    補充測試3,在查詢列上創建創建單獨的索引

      跟SQL Server 2012中一樣,執行計劃發生了變化 ,但是對於數據行的預估,同樣並沒有因為執行計劃的變化而(預估行數)變化。

      

      雖然執行計劃變了,但是對數據的預估並沒有變化,預估的演算法還是符合:預估行數 = P0*P11/2  * P21/4 * P31/8……* RowCount 

      

  

  在此可以看出,執行計劃對於(未超過統計信息範圍的情況下)數據行的預估,是有一定規律的,
  這個規律就是:
  SQL Server 2012 中,預估行數=p0*p1*p2*p3……*RowCount(Pn為查詢欄位的選擇性),
    SQL Server 2014 中,預估行數= P0*P11/2 * P21/4 * P31/8……* RowCount(Pn為查詢欄位的選擇性)。
  當然如果說統計信息過期或者取樣密度不夠,那就另當別論了,這個就關係到統計信息的更新策略問題了,也是一個非常大而且非常現實的問題,暫不深入展開討論。
  所以一開始我說暫不考慮統計信息自身是否理想,這裡是在統計信息非常完整的情況下做測試的。

 

  

  微軟為什麼在SQL Server 2014中,對非相關且未超出統計信息範圍的預估行數演算法做這麼一個變化,
  因為PN的值是小於1的
  預估行數的計算方法從p0*p1*p2*p3……*RowCount變化為P0*P11/2 * P21/4 * P31/8……* RowCount,顯然是增加了預估行數的大小,
  同時本文未提及的另外一種情況:對於超出統計信息範圍的情況下,新的預估方法也增加預估行數的大小,
  從整體上看,演算法是傾向於"估多不估少”的,有這麼一個改變
  至於為什麼要做出這個改變?
  如果經常做SQL優化的就會發現,不少問題都是少估了預期的數據行數(因為種種原因吧,這裡暫時不討論為什麼少估),
  造成執行SQL時分配的資源不夠,從而拖慢了SQL的執行效率
  一個非常典型的問題就是,預估的數據比實際的數據行數小,造成比如記憶體授予的不夠大,以及實際運算過程中採用不合理的執行計劃

  個人認為,(控制在一定範圍之內的)估多的情況下可以通過獲取更多的系統資源來提升SQL的執行效率,
  正常情況下也不會說是跟實際值差的太離譜造成資源的浪費。
  當然也有特殊情況,那就另當別論

  

  要註意的是我這裡有個前提,非相關的統計信息,不管是沒有任何索引,還是是創建和單列上的索引,對應的統計信息,都屬於非相關統計信息,
  如果創建複合索引(有人習慣叫組合索引),那麼執行計劃對於數據行的預估並不符合上述演算法,具體演算法我也不清楚。
  此種情況下,在SQL Server 2012和SQL Server 2014中預估演算法也不一樣,這個有機會再研究吧。

 

 

對於測試結果的補充說明:

  測試過程中一定要保證統計信息的完整性,以及取樣的百分比問題,理性情況下都是按照100%取樣的,
  中間我略去了一些細節問題,比如沒此測試之前都會 update statistics TestStatistic with fullscan,保證100%取樣。
  既然要精確到小數點後幾位,當然要求條件是理想情況下的,目的就是一定要排除其他條件對測試結果的影響。

 

  

總結:

本文通過一個簡單的示例,來瞭解了SQL Server通過統計信息對數據預估的計算方式和原理,以及SQL Server 2012和SQL Server2014之間的差異。
統計信息對於SQL執行計劃的選擇起著中樞神經般的作用,不光是在SQL Server資料庫中,包括其他關係資料庫,統計信息都是一個非常重要的資料庫對象。
可以說,SQL優化,統計信息以及與之息息相關的執行計劃是一個非常重要的因素,瞭解統計信息方面的知識對性能調優有著非常重要的作用。

在涉及到組合索引上的統計信息情況下,執行計劃對數據行的預估,SQL Server2012和SQL Server 2014中也不一樣,問題將會更加有趣,待有時間再寫吧。

 

 參考:Fanr_Zh 大神的 http://www.cnblogs.com/Amaranthus/p/3678647.html 

          以及 http://msdn.microsoft.com/en-us/library/dn673537.aspx 

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Swift - 通過疊加UILabel來實現混合的進度條 效果 源碼 https://github.com/YouXianMing/Swift-Animations ...
  • 原理 監聽ScrollView的滑動 源 碼 https://github.com/ln0491/TitleAlphaDemo ...
  • 我們需要監聽ScroView的滑動情況,比如滑動了多少距離,是否滑到佈局的頂部或者底部。可惜的是SDK並沒有相應的方法,不過倒是提供了一個 顯然這個方法是不能被外界調用的,因此就需要把它暴露出去,解決方式就是寫一個介面 然後重寫ScrollView類,給它提供上面寫的回調介面 佈局時,用重寫的這個S ...
  • Swift - 多個mask的動畫效果 效果 源碼 https://github.com/YouXianMing/Swift-Animations ...
  • RT 1.effective_prio函數計算進程的動態優先順序。 普通進程除了靜態優先順序,還有動態優先順序,其值的範圍是100(最高優先順序)~139(最低優先順序)。動態優先順序是調度程式在選擇新進程來運行的時候使用的數。它與靜態優先順序的關係用下麵的經驗公式表示: bonus是範圍從0-10的值,值小於5 ...
  • 該系列教程概述與目錄:http://www.cnblogs.com/chengyujia/p/5787111.html 一、知識點講解 當我們點擊系統自帶的按鈕時,按鈕的外觀會發生變化。上篇博文中我們畫了一個三角形按鈕,但點擊還不能變色,下麵我們就來實現點擊變色功能。從知識體繫上我們需要瞭解以下兩個 ...
  • 最近經過實際試驗,總結了InputType幾個常用取值表示的含義: 1、none, text, textVisiblePassword: 無任何輸入限制 2、textMultiLine: 允許多行輸入(預設EditText只允許單行輸入,即不換行) 3、textPassword: 無任何輸入限制,但 ...
  • 3、可能很多小伙伴是綠色的進入註冊表沒有ORACLE這個選項這就令人非常頭疼了。因為可能是32位的原因,你到註冊表的下麵看看HKEY_LOCAL_MACHINE->SOFTWARE->Wow6432Node看看有沒有ORACLE選項,如果還沒有那咱就添加一個;代碼如下建立二個尾碼名為AMERICAN ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...