前文
Index
第一個欄位至關重要它會影響資料統計值結果,Index
一般建立在查詢條件的欄位
每個
Index
都擁有自己的B+ tree
.
Index使用的資料結構(B+ tree)
B+ tree
是一種資料結構這個資料結構被Index
拿來使用,關於B+ tree
網路上有很多資源可再自行尋找,所以我們來談談為什麼DataBase
會使用B+ tree
在Wiki講述B+ tree
有其中一段
B+ tree
是能夠保持資料穩定有序,其插入與修改擁有較穩定的對數時間複雜度。B+ tree
元素由下而上插入,通過最大化在每個內部節點內的子節點的數目減少樹的高度,平衡操作不經常發生,而且效率增加了。這種價值得以確立通常需要每個節點在次級儲存中占據完整的磁碟塊或近似的大小。
簡白來說B+ tree
有一個特性是他會把資料存在子頁(Leaf Page)中透過一個參考把每個子頁串聯起來,提高穩定度.
B+ tree
資料結構如下圖,這個資料結在在範圍查詢時較B tree
來的更穩定
Index真正在使用B+ tree
儲存類似於下圖
此圖來自(Pro SQL Server Internals, 2nd edition)
Index優缺點
Index
可以加快查詢速度,因為Index
是以空間換取時間。
基本上它使用的資源如下:
- 每個
Index
都會建立一顆b+ tree
- 每次新增、更新資料時都會異動到有使用的
b+ tree
所以當你
Index
越多時,你需要維護的Index
越多(代表需要更多資源來維護)
建立太多Index
,小心降低(新增、更新)效率
因為SQLServer會對於這次新增、更新使用
Index
做異動
下面有一個T98
資料表擁有兩個Index
CIX_T98
Clustered IndexIX_T98
Convering Index
1 | IF EXISTS( |
當我們要執行更新COL1
欄位時(打開執行計畫)
1 | UPDATE T98 |
可以發現此次更新,我們會對於這兩個Index異動
CIX_T98
Clustered IndexIX_T98
Convering Index
1 | UPDATE T98 |
但當我只更新COL2
時,只會異動CIX_T98
Index
1 | UPDATE T98 |
這是為什麼?
因為
IX_T98
IndexB+ Tree
沒有包含COL2
欄位相關資訊,所以不需要更新
Clustered Index(叢集索引)
每個資料表只能有一個Clustered index
,資料表會依照Clustered index
方式存放排列資料,Clustered Index
會把資料放置在Left
子頁層
Cluster index
好比書籍頁碼目錄。每本書只能有一個目錄
建立Clustered Index
欄位有幾個重點
- 常用於查詢欄位
- 可識別度高(唯一性較高)
NonClustered Index(非叢集索引)
每個資料表能有許多NonClustered Index
,像每本書可以有很多種附錄
- 例如依照字母排序
- 依照附錄A
- 附錄B
NonClustered Index
按照Key Column
排序,
NonClustered Index
(index page)上所有分葉節點存放指標,如果資料表已存在Clustered Index
(KeyID
),那麼該指標將會指Clustered Index
,如不存在將指向資料真實存放位置(RID
)
this is a very important point to remember. Nonclustered indexes do not store information about physical row location when a table has a clustered index. They store the value of the clustered index key instead.
上面簡單來說如果NonClustered Index
沒有包含所有要查詢欄位
- 有
Clustered Index
,會執行Key Lookup
- 沒有
Clustered Index
,會執行RID Lookup
這裡的
RID
是指向真實資料位子RowID
如果Nonclustered index可以建立Unique盡量宣告成Unique,因為Leaf page就可以減少製造Unique的
Row-id
欄位,減少儲存空間.
RID Lookup
資料表沒有Clustered Index
且使用Index
所有查詢欄位不包含在Converting Index
中就會透過RID Lookup
查找確切Page上的Row(藉由Row-Id
)
RID Key的大小8 byte
lookup
會消耗Disk I/O
,所以消耗成本相對會比較大.
沒有
Clustered Index
的資料表我們稱為Heap
資料表
我們可以透過下面語法利用上圖 HEAP RID
找到資料儲存位置
1 | --HEAP RID:0x40110F0001002900 |
Heap RID Key:8 byte.(FID 2 Bytes,PID 4 Bytes,SLOT 2 Bytes)
Key Lookup
NonClustered Index
中會存放此Row在Clustered Index
相對位置,假如單單靠搜尋Non-Clustered Index
沒有辦法滿足所有查詢需要資料就會去Key Lookup
(by Clustered key)回找Clustered Index
取出相對應的資料.
Nonclustered Index Lookup I/O 操作計算公式
Index層數 + 讀取幾個Page + (幾筆資料 * clustered index層數)
此存取就稱為Lookup
,lookup
會消耗Disk I/O
,所以所耗的時間相對會比較大
在
NonClustered Index
中頁層會包含Clustered Index
的Key
範例演示
我們先準備10000筆樣本資料
1 | CREATE TABLE T( |
建立完資料後我們利用下面條件來查找資料.
1 | SELECT * |
因為沒有建立Index
,導致我明明只需要撈取一筆資料,但資料庫卻全表掃描
建立一個 NonClustered Index
我們在表中建立了一個NonClustered Index
,並利用相同查詢語法查詢資料
1 | CREATE NONCLUSTERED INDEX IX_T_Id on dbo.T( |
建立完NonClustered Index
後從原本的全表掃描變成RID Lookup
和Index Seek
,因為NonClustered Index
的B+ Tree
沒有包含所有需要撈取的資料.所以透過RID
回去Heap
資料表查找出所需要的欄位
RID Lookup
在執行計畫中呈現如下圖,
建立一個 Clustered Index
我們在T
資料表中建立一個Clustered Index
,並且執行相同查詢
1 | CREATE CLUSTERED INDEX CIX_T_UserId on dbo.T( |
能看到執行計畫的不同了,已經透過Key Lookup
回去查找資料,原因是目前資料表已經有Clustered Index
但此查詢使用條件使用NonClustered Index
所以導致需要Lookup
回去Clustered Index
的B+ Tree
查找資料
此文作者:Daniel Shih(石頭)
此文地址: https://isdaniel.github.io/dbindex-1/
版權聲明:本博客所有文章除特別聲明外,均採用 CC BY-NC-SA 3.0 TW 許可協議。轉載請註明出處!