SQL Server 非聚集索引(如何預先估算其大小空間)

来源:http://www.cnblogs.com/androidshouce/archive/2016/07/18/5680019.html
-Advertisement-
Play Games

假如有一張大表,現在需要增加一個非聚集索引,對於DBA來說,要有預估其大小以及執行時間的估算能力,尤其對一些企業使用SSD硬碟,其硬碟空間很是寶貴,增加索引如果錯誤預估其大小,很有可能導致硬碟資源超出預期使用量,造成沒必要的麻煩,這裡只針對其預估硬碟占用空間展開討論,行為標準8060 in_row_ ...


  

  假如有一張大表,現在需要增加一個非聚集索引,對於DBA來說,要有預估其大小以及執行時間的估算能力,尤其對一些企業使用SSD硬碟,其硬碟空間很是寶貴,增加索引如果錯誤預估其大小,很有可能導致硬碟資源超出預期使用量,造成沒必要的麻煩,這裡只針對其預估硬碟占用空間展開討論,行為標準8060 in_row_data,不涉及行溢出,大對象等情況。

  舉個例子給大家

  

複製代碼
create table Index_test (id int,a char(10))

go

insert into Index_test select 100,'aaaaa'

go 4000

create nonclustered index ix_id_a on Index_test (id,a)

go
複製代碼

 

      如果認為索引行的單行大小為4 + 10 = 14位元組,那麼最後的計算結果應該是

索引占用了(4000/(8096/14))*8192/1024 = 48KB。實際呢?

 Paddy

索引占用了(14+1)*8192/1024 = 120KB

 

與預期的值相差了2倍以上,設想一下,如果你的某張大表的索引錯誤的預測為50G,實際則會占用100G以上。

 

網路上很多資料給出了非聚集索引的存儲格式,這裡我簡單明瞭的說明下非聚集索引的內部結構和預測其大小的方法。:

 

(非聚集索引分為 根葉,中間級頁面,葉級頁面,實際在物理存儲上,我們可以將根葉和中間級頁面合併看成中間級頁面,因為兩者的存儲格式是相同的。我後面的討論也都是分為葉級頁面和中間級頁面)

 

非聚集索引葉級頁面單行:存儲格式

  1. 狀態位(1位元組),標識此行是否有變長,空值,以及此行是否為索引行等等

  2. 非聚集索引(定長列)鍵值大小

  3. 如果是堆表,則是Rowid(8位元組),如果是聚集索引,則是聚集索引鍵值(定長部分)大小。

  4. 包行列(定長列)長度

  5. 索引列數(2位元組)

  6. Null位(1位元組)用來標識哪列值為null

  7. 變長列數量(2位元組)

  8. 變長列1偏移長度(2位元組)+變長列2偏移長度(2位元組).. 變長列n偏移長度(2位元組)

  9. 包行列(變長列)長度 2位元組  

  10. 非聚集索引(變長列)鍵值大小 + 聚集索引(變長列)鍵值大小 +包行列(變長列)鍵值大小 

  11. 行偏移量(2位元組)

非聚集索引中間級頁面單行:存儲格式

  1. 狀態位(1位元組),標識此行是否有變長,空值,以及此行是否為索引行等等

  2. 非聚集索引(定長列)鍵值大小

  3. 如果是堆表,則是Rowid(8位元組),如果是聚集索引,則是聚集索引鍵值(定長部分)大小。

  4. 非聚集索引鍵值所在的pageid(4位元組) + 非聚集索引鍵值所在的頁面的文件id(2位元組)

  5. 索引列數(2位元組)

  6. Null位(1位元組)用來標識哪列值為null

  7. 變長列數量(2位元組)

  8. 變長列1偏移長度(2位元組)+變長列2偏移長度(2位元組).. 變長列n偏移長度(2位元組)

  9. 包行列(變長列)長度

  10. 非聚集索引(變長列)鍵值大小 + 聚集索引(變長列)鍵值大小 +包行列(變長列)鍵值大小

  11. 行偏移量(2位元組)

 

下麵總結了幾條規律,方便理解上面的結構:

  1. 如果索引涉及的列有一個允許null,則索引行會包含索引列數(2位元組)和Null位(1位元組)

  2. 如果索引包含聚集索引,則需將rowid替換為聚集索引鍵值

  3. 如果索引包含唯一約束,則中間層頁面不會包含rowid或者聚集索引鍵值

  4. 如果聚集索引不是唯一索引,而且存在重覆值,則重覆的聚集鍵值為(指定列 + 內部4位元組整數列)來標識唯一性,要點:內部4位元組整數列也屬於聚集索引鍵值,並且是變長列類型

  5. 如果沒有變長列,則在葉子頁面和中間層頁面不會包含:變長列數(2位元組) +變長列長度(2位元組)*變長列數 + 變長列鍵值

 

結論

   所以,對於一個非聚集索引來說,如果想確保內部系統開銷最小,索引行最節省空間,除了限制索引引用沒必要的列以外,還要考慮所有引用列均為not null,並且設置為unique唯一約束,同時最好具有聚集索引。 

 

預估非聚集索引行大小

  我根據以上規律總結了一個預估非聚集索引大小的腳本,因為索引填充率、變長列需要預先用最大值考慮等關係,最終結果會有稍微的誤差,只能當做最小預估空間的參考值。腳本並不能保證很完善,大家可以自行改良。

 文章開頭的例子,如果使用這個腳本,結果為:

和DMV輸出結果一樣

複製代碼
declare @fix_length                int    --定長欄位長度(byte)
declare @columns_count                int    --欄位數量
declare @variable_length            int    --變長欄位長度(byte)
declare @variable_count            int    --變長欄位數量
declare @Pri_Key_Length    int --聚集索引長度(byte)
declare @is_primarykey bit    --是否存在聚集索引,0不存在,1存在
declare @is_unique        bit    --是否指定唯一約束,0沒有指定,1為指定
declare @is_Null                bit    --是否允許為null,0為允許,1為不允許
declare @Num_Rows     int    --記錄數
declare @fillfactor            float    --填充因數,預設為100

set @fillfactor = 100
set @fix_length = 14
set @columns_count = 2
set @variable_length =0
set @variable_count = 0
set @Pri_Key_Length = 0
set @is_primarykey =0
set @is_unique =0
set @is_Null =0
set @Num_Rows  = 4000

declare @yezi                        int
declare @zhongjian            int
declare @ye_r_length            int
declare @ye_r_count            int 
declare @zhong_r_length    int
declare @zhong_r_count    int
--1.    無聚集索引,無唯一約束,允許null 
if @is_primarykey = 0 and @is_unique = 0 and @is_Null = 0
begin
    if @variable_count =0 
    begin
        set @ye_r_length= @fix_length + CEILING(@columns_count/8.0) + (1 + 8 + 2+2) 
        set @ye_r_count = (8192-96)*(@fillfactor/100.00) / @ye_r_length 
        set @yezi =  ceiling(@Num_Rows  / convert(float,convert(int,@ye_r_count)))
        set @zhong_r_length = @fix_length + CEILING(@columns_count/8.0) + (1 + 8 + 6 + 2 + 2)
        set @zhong_r_count =  (8192-96)/convert(int,@zhong_r_length)
        set @zhongjian = ceiling(@yezi/convert(float,(@zhong_r_count)))
    end
    else
    begin
        set @ye_r_length= @fix_length + CEILING(@columns_count/8.0) + (1 + 8 + 2 + 2 + 2*@variable_count + @variable_length + 2) 
        set @ye_r_count = (8192-96) *(@fillfactor/100.00)/ @ye_r_length 
        set @yezi =  ceiling(@Num_Rows  / convert(float,convert(int,@ye_r_count)))
        set @zhong_r_length = @fix_length + CEILING(@columns_count/8.0) + (1 + 8 + 6 + 2+2 + 2*@variable_count + @variable_length + 2)
        set @zhong_r_count =  (8192-96)/convert(int,@zhong_r_length)
        set @zhongjian = ceiling(@yezi/convert(float,(@zhong_r_count)))
    end
end
--2.    無聚集索引,無唯一約束,不允許null
if @is_primarykey = 0 and @is_unique = 0 and @is_Null = 1
begin
    if @variable_count = 0
    begin
            set @ye_r_length= @fix_length + (1 + 8 +2) 
            set @ye_r_count = (8192-96) *(@fillfactor/100.00) / @ye_r_length 
            set @yezi =  ceiling(@Num_Rows  / convert(float,convert(int,@ye_r_count)))
            set @zhong_r_length = @fix_length +   (1 + 8 + 6   + 2)
            set @zhong_r_count =  (8192-96)/convert(int,@zhong_r_length)
            set @zhongjian = ceiling(@yezi/convert(float,(@zhong_r_count)))
    end
    else
    begin
            set @ye_r_length= @fix_length + (1 + 8 + 2 + 2*@variable_count + @variable_length + 2) 
            set @ye_r_count = (8192-96)*(@fillfactor/100.00) / @ye_r_length 
            set @yezi =  ceiling(@Num_Rows  / convert(float,convert(int,@ye_r_count)))
            set @zhong_r_length = @fix_length +   (1 + 8 + 6 + 2*@variable_count + @variable_length    + 2)
            set @zhong_r_count =  (8192-96) /convert(int,@zhong_r_length)
            set @zhongjian = ceiling(@yezi/convert(float,(@zhong_r_count)))
    end
end
--3.    無聚集索引,有唯一約束,允許null 
if @is_primarykey = 0 and @is_unique = 1 and @is_Null = 0
begin
    if @variable_count  = 0
    begin
        set @ye_r_length= @fix_length + CEILING(@columns_count/8.0) + (1 + 8 + 2+2) 
        set @ye_r_count = (8192-96) *(@fillfactor/100.00)/ @ye_r_length 
        set @yezi =  ceiling(@Num_Rows  / convert(float,convert(int,@ye_r_count)))
        set @zhong_r_length = @fix_length + CEILING(@columns_count/8.0) + (1 + 6 + 2 + 2)
        set @zhong_r_count =  (8192-96)*0.97/convert(int,@zhong_r_length)
        set @zhongjian = ceiling(@yezi/convert(float,(@zhong_r_count)))
    end
    else
    begin
        set @ye_r_length= @fix_length + CEILING(@columns_count/8.0) + (1 + 8 + 2 + 2 + 2*@variable_count + @variable_length +2) 
        set @ye_r_count = (8192-96) *(@fillfactor/100.00)/ @ye_r_length 
        set @yezi =  ceiling(@Num_Rows  / convert(float,convert(int,@ye_r_count)))
        set @zhong_r_length = @fix_length + CEILING(@columns_count/8.0) + (1 + 6 + 2 +2 + 2*@variable_count + @variable_length + 2)
        set @zhong_r_count =  (8192-96)*0.86/convert(int,@zhong_r_length)
        set @zhongjian = ceiling(@yezi/convert(float,(@zhong_r_count)))
    end
end

--4.    無聚集索引,有唯一約束,不允許null 
if @is_primarykey = 0 and @is_unique = 1 and @is_Null = 1
begin
    if @variable_count = 0
    begin
        set @ye_r_length= @fix_length + (1 + 8 +2) 
        set @ye_r_count = (8192-96*(@fillfactor/100.00)) / @ye_r_length 
        set @yezi =  ceiling(@Num_Rows  / convert(float,convert(int,@ye_r_count)))
        set @zhong_r_length = @fix_length +   (1 + 6   + 2)
        set @zhong_r_count =  (8192-96)/convert(int,@zhong_r_length)
        set @zhongjian = ceiling(@yezi/convert(float,(@zhong_r_count)))
    end
    else
    begin
        set @ye_r_length= @fix_length + (1 + 8 + 2 + 2*@variable_count  + @variable_length + 2) 
        set @ye_r_count = (8192-96)*(@fillfactor/100.00) / @ye_r_length 
        set @yezi =  ceiling(@Num_Rows  / convert(float,convert(int,@ye_r_count)))
        set @zhong_r_length = @fix_length +   (1 + 6  + 2 + 2*@variable_count  + @variable_length  + 2)
        set @zhong_r_count =  (8192-96)/convert(int,@zhong_r_length)
        set @zhongjian = ceiling(@yezi/convert(float,(@zhong_r_count)))
    end
end

--5.    有聚集索引,無唯一約束,允許null 
if @is_primarykey = 1 and @is_unique = 0 and @is_Null = 0
begin
    if @variable_count = 0
    begin
            set @ye_r_length= @fix_length + @Pri_Key_Length+CEILING(@columns_count/8.0) + (1 + 2+2) 
            set @ye_r_count = (8192-96)*(@fillfactor/100.00) / @ye_r_length 
            set @yezi =  ceiling(@Num_Rows  / convert(float,convert(int,@ye_r_count)))
            set @zhong_r_length = @fix_length +@Pri_Key_Length+ CEILING(@columns_count/8.0) + (1  + 6 + 2  + 2)
            set @zhong_r_count =  (8192-96)/convert(int,@zhong_r_length)
            set @zhongjian = ceiling(@yezi/convert(float,(@zhong_r_count)))
    end
    else
    begin
            set @ye_r_length= @fix_length + @Pri_Key_Length+CEILING(@columns_count/8.0) + (1 + 2+ 2 + 2*@variable_count +@variable_length +2) 
            set @ye_r_count = (8192-96)*(@fillfactor/100.00) / @ye_r_length 
            set @yezi =  ceiling(@Num_Rows  / convert(float,convert(int,@ye_r_count)))
            set @zhong_r_length = @fix_length +@Pri_Key_Length+ CEILING(@columns_count/8.0) + (1  + 6 + 2 + 2 + 2*@variable_count +@fix_length + 2)
            set @zhong_r_count =  (8192-96)/convert(int,@zhong_r_length)
            set @zhongjian = ceiling(@yezi/convert(float,(@zhong_r_count)))
    end
end


--6.    有聚集索引,無唯一約束,不允許null 
if @is_primarykey = 1 and @is_unique = 0 and @is_Null = 1
begin
    if @variable_count = 0
    begin
        set @ye_r_length= @fix_length +@Pri_Key_Length+ (1 +2) 
        set @ye_r_count = (8192-96)*(@fillfactor/100.00) / @ye_r_length 
        set @yezi =  ceiling(@Num_Rows  / convert(float,convert(int,@ye_r_count)))
        set @zhong_r_length = @fix_length +@Pri_Key_Length+   (1 + 6   + 2)
        set @zhong_r_count =  (8192-96)/convert(int,@zhong_r_length)
        set @zhongjian = ceiling(@yezi/convert(float,(@zhong_r_count)))
    end
    else
    begin
        set @ye_r_length= @fix_length +@Pri_Key_Length+ (1 + 2 + 2*@variable_count  + @variable_length  +2) 
        set @ye_r_count = (8192-96)*<

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

-Advertisement-
Play Games
更多相關文章
  • 本文是redis學習系列的第四篇,前面我們學習了redis的數據結構和一些高級特性,點擊下麵鏈接可回看 《詳細講解redis數據結構(記憶體模型)以及常用命令》 《redis高級應用(主從、事務與鎖、持久化)》 本文我們繼續學習redis的高級特性——集群。本文主要內容包括集群搭建、集群分區原理和集群 ...
  • 一下的示例所用用戶名和密碼為:test,111111 Mysql密碼修改: Mysql修改密碼需要root的許可權,先執行mysql -uroot -p(密碼); 1)使用set password方式來修改賬戶密碼: set password for ‘hzd’@’localhost’=passwor ...
  • 1、準備 cmake-3.6.0.tar.gz bison-3.0.4.tar.gz mysql-5.7.13.tar.gz (http://dev.mysql.com/get/Downloads/MySQL-5.7/mysql-5.7.13.tar.gz) 2、安裝cmake 和 bison 首先 ...
  • MYSQL資料庫基礎: 資料庫幫我們解決以下數據存取難題: 較大數據量 事務控制 持久化和數據安全 高性能要求 高併發訪問 關係型:mysql,oracle,sql server,postgresql 非關係型:Hadoop(大數據),monogoDB(文檔型 ),redis(鍵值),casscan ...
  • 一、phoenix的簡介 hbase的java api或者其語法很難用,可以認為phoenix是一個中間件,提供了訪問hbase的另外的語法。 二、配置phoenix和hbase 1.下載 phoenix的官網是:http://phoenix.apache.org/,用戶可以到該網址找到對應hbas ...
  • 使用同義詞和insert..from..語句,解決訂閱庫數據丟失的問題 ...
  • SQL分類: DDL—數據定義語言(CREATE,ALTER,DROP,DECLARE) DML—數據操縱語言(SELECT,DELETE,UPDATE,INSERT) DCL—數據控制語言(GRANT,REVOKE,COMMIT,ROLLBACK) 首先,簡要介紹基礎語句: 1、說明:創建資料庫  ...
  • Oracle text-Oracle Text的體系架構 一、 Oracle Text 索引文檔時所使用的主要邏輯步驟如下: (1)數據存儲邏輯搜索表的所有行,並讀取列中的數據。通常,這隻是列數據,但有些數據存儲使用列數據作為文檔數據的指針。例如,URL_DATASTORE 將列數據作為URL使用。 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...