SQL Server - 彙集索引 <第六篇>

  彙集索引的葉子頁存儲的就是表的數據。所以,錶行物理上按照彙集索引列排序,由於表數據只能有一種物理順序,因此一個表只能有一個彙集索引。數據庫

  當咱們建立主鍵約束時,若是不存在彙集索引而且該索引沒有被明確指定爲非彙集索引,SQL Server會自動將其建立爲惟一的彙集索引,這並非說主鍵列就必定是彙集索引,這只是默認行爲而已。併發

  示例,建表時經過指定主鍵爲非彙集索引使主鍵列不爲彙集列:post

CREATE TABLE MyTableKeyExample
{
  Column1 int IDENTITY PRIMARY KEY NONCLUSTERED,
  Column2 int 
}

1、堆表與彙集表

  沒有彙集索引的表稱爲堆表。堆表的數據列沒有任何特別的順序,鏈接到表的相鄰頁面。與訪問大的彙集表相比,對標這種無組織的結構一般增大了訪問大的堆表的開銷。性能

  有彙集索引的表稱爲彙集表,彙集表是B樹結構,數據量大時,可以大幅減小讀次數。字體

2、與非彙集索引的關係

  SQL Server中彙集索引和非彙集索引之間有一個有趣的關係,非彙集索引的一個索引列包含指向表的對應數據行的指針。這個指針被稱爲行定位器。行定位器的值取決於數據表是堆表仍是彙集表。當時堆表時,行定位器是指向堆中數據行的RID指針。對於具備彙集索引的表,行定位器是彙集索引鍵值。優化

  下面用一個表格來講明這種關係spa

  假設有一個2列的表:3d

RID(這不是實際列) 列1 列2
1 A1 A2
2 B1 B2

  堆表:  指針

索引列(列1) 行定位器
A1 RID = 1 指向表中第一行數據
B1 RID = 2 指向表中第二行數據

  彙集表,假設咱們將列2設爲彙集索引列:code

索引列(列1) 行定位器
A1 A2 指向彙集鍵
B1 B2 指向彙集鍵

  因而可知,經過非彙集索引列查找一行數據,還須要多一步-經過RID得到實際數據。這個RID在堆表是行指針,在彙集表是彙集鍵值。

3、彙集索引的建議

  一、首先建立彙集索引

  對於彙集表而言,由於全部非彙集索引在其索引行上都保存一個彙集索引鍵值,因此非彙集索引和彙集索引建立的順序很是重要。若是非彙集索引先於彙集索引建立,那麼非彙集索引的行定位器將包含指向堆表的RID的指針。而後再建立彙集索引時,會將全部非彙集索引的RID指針改成彙集鍵,這實際上至關於從新創建了非彙集索引。
  爲了最好的性能,最好在建立任何非彙集索引以前建立彙集索引。這將使得非彙集索引在建立的時候將他們的行定位器直接設置爲彙集索引值。這對最終的性能沒有太大影響,可是SQL Server工做量少不少,速度快不少。若是你是在線上運行着的系統進行維護操做,這尤爲有用。

  二、保持窄索引

  由於全部的非彙集索引將彙集索引鍵做爲行定位器,爲了最佳的性能,應使彙集索引的整體長度儘量小。

  試想,假如建立了個寬的彙集索引,如CHAR(500),這將在每一個非彙集索引中添加一個500字節的值。就算非彙集索引什麼都不放,光彙集索引鍵值佔用的空間,它一頁的數據頁僅僅能存放16個數據行左右。

  保持窄彙集索引能有效減小邏輯讀操做與磁盤I/O。

  三、一步重建彙集索引

  由於彙集索引上有非彙集索引的依賴性,用單獨的DROP INDEX 和 CREATE INDEX語句重建彙集索引將致使全部非彙集索引被重建兩次(DROP,行定位器指向堆表數據行指針,CREATE行定位器指向新的彙集鍵值)。爲了不這種狀況,使用CREATE INDEX語句的DROP_EXISTING子句來在一個單獨的原子步驟中重建彙集索引。類似地,也能夠在非彙集索引上使用DROP_EXISTING子句。

  CREATE CLUSTERED INDEX index1 ON PersonTenThousand(Id) WITH (DROP_EXISTING = ON) 

  四、什麼時候使用匯集索引

  在某些狀況下,使用匯集索引是很是有幫助的。

  一、檢索必定範圍的數據

  由於彙集索引的葉子頁面就是表的實際數據,彙集索引列的順序就是表中數據行的物理順序。若是數據行的物理順序與查詢請求的數據順序相同,磁盤刺頭能夠順序地讀取全部行,而不須要太多的磁頭移動。

  假設我彙集索引創建在ID列,我須要讀取ID BETWEEN 1 AND 100或ID > 100的數據,那麼全部數據行在磁盤上排列在一塊兒。這使磁頭能夠移動到磁盤上第一行的位置,而後用最少的磁頭移動順序讀出全部數據。另外一方面,若是行在磁盤上沒有以正確的物理順序排列,磁頭必須隨機地從一個位置移動到另外一個位置來讀取全部相關的行。磁頭的物理移動是磁盤操做開銷的最主要部分,將行以合適的物理順序在磁盤上排序(使用匯集索引)優化了I/O開銷。

  二、讀取預先排序的數據

  彙集索引在數據讀取須要排序時特別有效,若是在可能須要排序的一列或多列上建立一個彙集索引,那麼行將被按該順序物理排序,這消除了數據讀取以後排序的開銷。

  在沒有彙集索引的狀況下,檢索範圍排序的數據:

  

  在有彙集索引的狀況下,檢索範圍排序的數據:

  

  從中看到,有彙集索引的範圍排序返回數據很是快速,由於對於彙集列,自己就是已經排好順序存放於數據庫中的。

  五、什麼時候不使用匯集索引

  在某些狀況下,最好不使用匯集索引。

  一、頻繁更新的列

  若是彙集索引列頻繁更新,將致使全部非彙集索引行的行定位器相應更新,從而顯著地增長相關操做查詢的開銷。還將阻塞這段時間引用相同部分和非彙集索引的其餘查詢,從而影響數據庫的並行性。所以,應該避免在大量更新的列上建立彙集索引。

  二、寬的關鍵字

  由於全部非彙集索引將彙集鍵做爲其行定位器,因此爲了性能,應該避免在很是寬或太多列上建立彙集索引。上面紅色加粗字體特別說明了緣由。

  三、太多並行的順序插入

  若是但願併發地添加許多新行,那麼對於性能來說,將他們分佈到表的各個數據頁面更好一些。可是,若是將全部行按照與彙集索引相同的順序添加,那麼全部的插入操做都在表的最後一個頁面上進行。這可能在磁盤的對應山區形成一個巨大的「熱點」,爲了不磁盤熱點,不該該將數據行按照物理位置相同的順序排列。能夠經過建立另外一列上的索引(該索引不會將行按照新航相同的順序來排列)來插入操做隨機地分佈到整個表。這個問題只在大量同時插入時發生。

  容許在表的尾部插入,可以避免須要容納新行時發生的頁拆分。若是並行插入數據下降,那麼按照新行的順序來排列數據行(使用匯集索引)將避免頁拆分。可是,若是磁盤熱點成爲性能瓶頸,那麼新行能夠經過下降表的填充因子來容納到中間頁面。另外,「熱」的頁面將在內存中,這也有利於性能。

 最後附上一個設置非主鍵爲彙集索引列的方法:

1. 查看全部的索引,默認狀況下主鍵上都會創建彙集索引

  查看索引:
  sp_helpindex person
  查看約束:
  sp_helpconstraint person

2. --刪除主鍵約束,把【1】中查詢出的主鍵上的索引約束【如:PK__person__117F9D94】去除掉。去掉主鍵字段上面的主鍵約束,此時該字段不是主鍵了。
  alter table person drop constraint PK_Person

3.--建立彙集索引到其它列

  create clustered index test_index on person(Name)

4.—修改原來的主鍵字段仍是爲主鍵,此時會自動創建非彙集索引【由於已經有了彙集索引】

sp_helpindex person
sp_helpconstraint person
alter table person drop constraint PK_Person
create clustered index test_index on person(Name)
alter table person add primary key (id)

  alter table person add primary key (id)

  

相關文章
相關標籤/搜索