爲了更好地理解覆蓋索引,在正式介紹覆蓋索引以前,首先稍微來談一談有關索引的一些基礎知識。html
在SQLServer中,數據存儲的基本單位是頁,一頁的大小爲8KB,分別由頁首,數據行和行偏移量組成,以下圖結構:數據庫
頁首固定佔用96個字節,用來存儲相關的頁面系統信息,例如所屬的數據庫表對象Id等。數據行是真實數據的存儲區域,每一行的大小是不固定的。行偏移量是一個數組,數組的每一個位置佔2個字節,用來存儲數據行距離開頭的位置偏移量,主要是用來作快速定位,例如想要查找第N行,只要訪問行偏移量數組的第N項,就能快速找到數據行所在的位置。索引頁和數據頁的結構相似,所不一樣的是索引頁的數據行存儲的是和索引相關的信息。數組
彙集索引定義了表中數據存儲的真實物理位置,它是按照指定列的順序來存儲數據的,類比於新華字典中的漢字是按照拼音順序排列的,因此每張表只能創建一個彙集索引。彙集索引是一棵B+樹結構,包含索引頁和數據頁,最底下的一排葉子節點是數據頁,往上則爲索引頁,來看一張圖應該更清晰一些:性能優化
非彙集索引是獨立於數據真實存儲順序邏輯而存在的,類比於新華字典中按偏旁部首查找漢字的方式。與彙集索引對比,非彙集索引也是B+樹的數據結構,但卻只包含索引頁,並且在一張表中能夠創建多個非彙集索引,有關索引的深刻分析能夠查看這篇文章。一樣來看一張非彙集索引的圖:數據結構
覆蓋索引是在SQLServer2005中引入的概念,只能創建在非彙集索引的基礎上,一般狀況下,非彙集索引的索引頁是不包含真實數據的,只存儲着指向數據頁中數據行的指針,而覆蓋索引則是經過將數據存儲在索引頁上,從而在查找對應數據的時候,只要找到索引頁就能夠訪問到數據,無需再去查詢數據頁,因此說這個索引是數據「覆蓋」的。ide
--覆蓋索引的建立是在非彙集索引建立的基礎上增長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的同時開啓顯示實際的執行計劃,從而能夠清楚得看到對比結果。spa
SELECT t1_name FROM t1 WHERE t1_id = 500; SELECT t1_name1 FROM t1 WHERE t1_id =500;
查詢1開銷爲33%,而查詢2的開銷爲67%,對比能夠看到查詢t1_name的開銷比查詢t1_name1的開銷小不少,由於查詢t1_name只須要執行索引,就能夠在索引頁上找到數據,而查詢t1_name1還要去查找數據頁。
建立索引能帶來查詢的優化,但卻帶來了更改數據的負擔,覆蓋索引也不意外。由上面的分析咱們知道,覆蓋索引是非彙集索引的進一步細化,在更新數據的時候,若是涉及到覆蓋索引INCLUDE的列,除了更改數據頁以外還要更改索引頁,比單純使用非彙集索引增添了額外的工做。因此,在設計覆蓋索引的時候,要綜合考慮應該覆蓋的列,確保INCLUDE的列能帶來最佳的性能優化。