SQLServer覆蓋索引


  為了更好地理解覆蓋索引,在正式介紹覆蓋索引之前,首先稍微來談一談有關索引的一些基礎知識。

數據頁和索引頁

  在SQLServer中,數據存儲的基本單位是頁,一頁的大小為8KB,分別由頁首,數據行和行偏移量組成,如下圖結構:

  

  頁首固定占用96個字節,用來存儲相關的頁面系統信息,例如所屬的數據庫表對象Id等。數據行是真實數據的存儲區域,每一行的大小是不固定的。行偏移量是一個數組,數組的每個位置占2個字節,用來存儲數據行距離開頭的位置偏移量,主要是用來做快速定位,例如想要查找第N行,只要訪問行偏移量數組的第N項,就能快速找到數據行所在的位置。索引頁和數據頁的結構類似,所不同的是索引頁的數據行存儲的是和索引相關的信息。

聚集索引和非聚集索引

  聚集索引定義了表中數據存儲的真實物理位置,它是按照指定列的順序來存儲數據的,類比於新華字典中的漢字是按照拼音順序排列的,所以每張表只能建立一個聚集索引。聚集索引是一棵B+樹結構,包含索引頁和數據頁,最底下的一排葉子節點是數據頁,往上則為索引頁,來看一張圖應該更清晰一些:

  

  非聚集索引是獨立於數據真實存儲順序邏輯而存在的,類比於新華字典中按偏旁部首查找漢字的方式。與聚集索引對比,非聚集索引也是B+樹的數據結構,但卻只包含索引頁,而且在一張表中可以建立多個非聚集索引,有關索引的深入分析可以查看這篇文章。同樣來看一張非聚集索引的圖:

  

什么是覆蓋索引

  覆蓋索引是在SQLServer2005中引入的概念,只能建立在非聚集索引的基礎上,通常情況下,非聚集索引的索引頁是不包含真實數據的,只存儲着指向數據頁中數據行的指針,而覆蓋索引則是通過將數據存儲在索引頁上,從而在查找對應數據的時候,只要找到索引頁就可以訪問到數據,無需再去查詢數據頁,所以說這個索引是數據“覆蓋”的。

--覆蓋索引的創建是在非聚集索引創建的基礎上增加INCLUDE語句
CREATE NONCLUSTERED INDEX {index_name}
ON {table_name}(column_name...) --非聚集索引可以聲明指定多個列作為索引項
INCLUDE(column_name...)         --覆蓋索引可以指定多個列存儲在索引頁上
創建覆蓋索引的語法

覆蓋索引分析

  這一小節將通過創建覆蓋索引以及使用DBCC命令查看索引的方式進行介紹。

IF DB_ID('Test') IS NULL
BEGIN
    CREATE DATABASE Test;
END
GO

USE Test;
GO

IF OBJECT_ID('t1','U') IS NULL
BEGIN
    CREATE TABLE t1
    (
        t1_id    INT         NOT NULL IDENTITY(1,1),
        t1_name  VARCHAR(20) NOT NULL,
        t1_name1 VARCHAR(20) NOT NULL,
        t1_name2 VARCHAR(20) NOT NULL,
        t1_name3 VARCHAR(20) NOT NULL,
        t1_name4 VARCHAR(20) NOT NULL,
        t1_name5 VARCHAR(20) NOT NULL
    );
END

/*
*插入測試數據
*/
INSERT INTO t1 
(   t1_name,
    t1_name1,
    t1_name2,
    t1_name3,
    t1_name4,
    t1_name5    )
    SELECT 'name',
           'name',
           'name',
           'name',
           'name',
           'name'
      FROM sysobjects o1
CROSS JOIN sysobjects o2;     

/*
*創建覆蓋索引
*/
IF NOT EXISTS(SELECT 1
                FROM sysindexes
               WHERE name='idx_t1_id')
BEGIN 
    CREATE NONCLUSTERED INDEX idx_t1_id
    ON t1(t1_id)
    INCLUDE(t1_name);
END
創建表&測試數據&覆蓋索引

  執行CROSS JOIN插入的測試數據有4000條左右,現在可以使用DBCC命令來查看表的數據頁和索引頁的情況。

/*
*查看頁的基本信息
*前提條件:表中必須插入了數據
*所需參數:(數據庫名,表名,-1表示顯示全部IAM頁,數據頁, 索引頁)
*/
DBCC IND (Test,t1,-1); 
查看表的數據頁和索引頁命令

  執行完這條命令后,應該可以看到顯示的頁信息,其中PageType=1的行表示數據頁,PageType=2的行表示索引頁,任意選擇一條PageType=2的行,找到PageFID和PagePID,就可以使用DBCC命令來查看索引頁的具體信息。

/*
*查看索引頁的基本信息
*所需參數:(數據庫名,PageFID,PagePID,3表示輸出每行每列的信息)
*/
DBCC PAGE(Test,1,7732,3);
查看索引頁信息的命令

   執行完這條命令后,應該可以看到t1_name這一列的信息是包含在這個索引頁中的。現在可以通過執行不同的查詢SQL來查看覆蓋索引所帶來的性能提升,在執行SQL的同時開啟顯示實際的執行計划,從而可以清楚得看到對比結果。

SELECT t1_name
  FROM t1
 WHERE t1_id = 500;

SELECT t1_name1
  FROM t1
 WHERE t1_id =500;
對比查詢SQL

  

  

  查詢1開銷為33%,而查詢2的開銷為67%,對比可以看到查詢t1_name的開銷比查詢t1_name1的開銷小很多,因為查詢t1_name只需要執行索引,就可以在索引頁上找到數據,而查詢t1_name1還要去查找數據頁。

覆蓋索引的思考

  創建索引能帶來查詢的優化,但卻帶來了更改數據的負擔,覆蓋索引也不意外。由上面的分析我們知道,覆蓋索引是非聚集索引的進一步細化,在更新數據的時候,如果涉及到覆蓋索引INCLUDE的列,除了更改數據頁之外還要更改索引頁,比單純使用非聚集索引增添了額外的工作。所以,在設計覆蓋索引的時候,要綜合考慮應該覆蓋的列,確保INCLUDE的列能帶來最佳的性能優化。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM