在SQL Server中,索引是一種加強式的存在,這意味着,即便沒有索引,SQL Server仍然能夠實現應有的功能。但索引能夠在大多數狀況下大大提高查詢性能,在OLAP中尤爲明顯.要徹底理解索引的概念,須要瞭解大量原理性的知識,包括B樹,堆,數據庫頁,區,填充因子,碎片,文件組等等一系列相關知識,這些知識寫一本小書也不爲過。因此本文並不會深刻討論這些主題。數據庫
索引是對數據庫表中一列或多列的值進行排序的一種結構,使用索引可快速訪問數據庫表中的特定信息。工具
精簡來講,索引是一種結構.在SQL Server中,索引和表(這裏指的是加了彙集索引的表)的存儲結構是同樣的,都是B樹,B樹是一種用於查找的平衡多叉樹.理解B樹的概念以下圖:性能
理解爲何使用B樹做爲索引和表(有彙集索引)的結構,首先須要理解SQL Server存儲數據的原理.學習
在SQL SERVER中,存儲的單位最小是頁(PAGE),頁是不可再分的。就像細胞是生物學中不可再分的,或是原子是化學中不可再分的最小單位同樣.這意味着,SQL SERVER對於頁的讀取,要麼整個讀取,要麼徹底不讀取,沒有折中.spa
在數據庫檢索來講,對於磁盤IO掃描是最消耗時間的.由於磁盤掃描涉及不少物理特性,這些是至關消耗時間的。因此B樹設計的初衷是爲了減小對於磁盤的掃描次數。若是一個表或索引沒有使用B樹(對於沒有彙集索引的表是使用堆heap存儲),那麼查找一個數據,須要在整個表包含的數據庫頁中全盤掃描。這無疑會大大加劇IO負擔.而在SQL SERVER中使用B樹進行存儲,則僅僅須要將B樹的根節點存入內存,通過幾回查找後就能夠找到存放所需數據的被葉子節點包含的頁!進而避免的全盤掃描從而提升了性能.設計
下面,經過一個例子來證實:指針
在SQL SERVER中,表上若是沒有創建彙集索引,則是按照堆(HEAP)存放的,假設我有這樣一張表:code
如今這張表上沒有任何索引,也就是以堆存放,我經過在其上加上彙集索引(以B樹存放)來展示對IO的減小:blog
在SQL SERVER中,最主要的兩類索引是彙集索引和非彙集索引。能夠看到,這兩個分類是圍繞彙集這個關鍵字進行的.那麼首先要理解什麼是彙集.排序
彙集在索引中的定義:
爲了提升某個屬性(或屬性組)的查詢速度,把這個或這些屬性(稱爲彙集碼)上具備相同值的元組集中存放在連續的物理塊稱爲彙集。
簡單來講,彙集索引就是:
在SQL SERVER中,彙集的做用就是將某一列(或是多列)的物理順序改變爲和邏輯順序相一致,好比,我從adventureworks數據庫的employee中抽取5條數據:
當我在ContactID上創建彙集索引時,再次查詢:
在SQL SERVER中,彙集索引的存儲是以B樹存儲,B樹的葉子直接存儲彙集索引的數據:
由於彙集索引改變的是其所在表的物理存儲順序,因此每一個表只能有一個彙集索引.
由於每一個表只能有一個彙集索引,若是咱們對一個表的查詢不只僅限於在彙集索引上的字段。咱們又對彙集索引列以外還有索引的要求,那麼就須要非彙集索引了.
非彙集索引,本質上來講也是彙集索引的一種.非彙集索引並不改變其所在表的物理結構,而是額外生成一個彙集索引的B樹結構,但葉子節點是對於其所在表的引用,這個引用分爲兩種,若是其所在表上沒有彙集索引,則引用行號。若是其所在表上已經有了彙集索引,則引用匯集索引的頁.
一個簡單的非彙集索引概念以下:
能夠看到,非彙集索引須要額外的空間進行存儲,按照被索引列進行彙集索引,並在B樹的葉子節點包含指向非彙集索引所在表的指針.
MSDN中,對於非彙集索引描述圖是:
能夠看到,非彙集索引也是一個B樹結構,與彙集索引不一樣的是,B樹的葉子節點存的是指向堆或彙集索引的指針.
經過非彙集索引的原理能夠看出,若是其所在表的物理結構改變後,好比加上或是刪除彙集索引,那麼全部非彙集索引都須要被重建,這個對於性能的損耗是至關大的。因此最好要先創建彙集索引,再創建對應的非彙集索引.
前面經過對於彙集索引和非彙集索引的原理解釋.咱們不難發現,大多數狀況下,彙集索引的速度比非彙集索引要略快一些.由於彙集索引的B樹葉子節點直接存儲數據,而非彙集索引還須要額外經過葉子節點的指針找到數據.
還有,對於大量連續數據查找,非彙集索引十分乏力,由於非彙集索引須要在非彙集索引的B樹中找到每一行的指針,再去其所在表上找數據,性能所以會大打折扣.有時甚至不如不加非彙集索引.
所以,大多數狀況下彙集索引都要快於非彙集索引。但彙集索引只能有一個,所以選對彙集索引所施加的列對於查詢性能提高相當緊要.
索引的使用並不須要顯式使用,創建索引後查詢分析器會自動找出最短路徑使用索引.
可是有這種狀況.當隨着數據量的增加,產生了索引碎片後,不少存儲的數據進行了不適當的跨頁,會形成碎片(關於跨頁和碎片以及填充因子的介紹,我會在後續文章中說到)咱們須要從新創建索引以加快性能:
好比前面的test_tb2上創建的一個彙集索引和非彙集索引,能夠經過DMV語句查詢其索引的狀況:
SELECT index_type_desc,alloc_unit_type_desc,avg_fragmentation_in_percent,fragment_count,avg_fragment_size_in_pages,page_count,record_count,avg_page_space_used_in_percentFROM sys.dm_db_index_physical_stats(DB_ID('AdventureWorks'),OBJECT_ID('test_tb2'),NULL,NULL,'Sampled')
咱們能夠經過重建索引來提升速度:
還有一種狀況是,當隨着表數據量的增大,有時候須要更新表上的統計信息,讓查詢分析器根據這些信息選擇路徑,使用:
UPDATE STATISTICS 表名
那麼何時知道須要更新這些統計信息呢,就是當執行計劃中估計行數和實際表的行數有出入時:
我最喜歡的一句話是」everything has price」。咱們經過索引得到的任何性能提高並非不須要付出代價。這個代價來自幾方面.
1.經過彙集索引的原理咱們知道,當表創建索引後,就以B樹來存儲數據.因此當對其進行更新插入刪除時,就須要頁在物理上的移動以調整B樹.所以當更新插入刪除數據時,會帶來性能的降低。而對於彙集索引,當更新表後,非彙集索引也須要進行更新,至關於多更新了N(N=非彙集索引數量)個表。所以也降低了性能.
2.經過上面對非彙集索引原理的介紹,能夠看到,非彙集索引須要額外的磁盤空間。
3.前文提過,不恰當的非彙集索引反而會下降性能.
因此使用索引須要根據實際狀況進行權衡.一般我都會將非彙集索引所有放到另一個獨立硬盤上,這樣能夠分散IO,從而使查詢並行.
本文從索引的原理和概念對SQL SERVER中索引進行介紹,索引是一個很強大的工具,也是一把雙刃劍.對於恰當使用索引須要對索引的原理以及數據庫存儲的相關原理進行系統的學習.