1. 程式人生 > >數據庫存儲結構:頁、聚集索引、非聚集索引

數據庫存儲結構:頁、聚集索引、非聚集索引

創建 方法 6.2 insert語句 方式 放置 變化 分支 通過

數據庫存儲結構:頁、聚集索引、非聚集索引

想了解數據庫存儲結構,因先了解數據庫的訪問方式然後從原理上理解數據庫存儲結構方式。

一、SQL Server中訪問數據的方式

從廣義上講,SQL Server檢索所需數據的方法只有兩種:

(1)使用全表掃描

(2)使用索引

 1、使用全表掃描

  表掃描是相當直觀。當執行表掃描時,SQL Server從表的物理起點處開始,瀏覽表中的每一行。當發現和查詢條件匹配的行時,就在結果集中包含它們。關於表掃描很多說法都是效率低,但是如果表數據減少的情況下,實際上使用表掃描卻是最快的。

 2、使用索引

  在查詢優化過程中,優化器查看所有可用的索引結構並且選擇最好的一個(這主要基於在連接和WHERE子句中所指定的信息,以及SQL Server在索引結構中保存的統計信息)。一旦選擇了索引,SQL Server將在樹結構中導航至與條件匹配的數據位置,並且只提取它所需的記錄。與表掃描的區別在於,因為數據時排序的,所以查詢引擎知道它何時到達正在查找的當前範圍的下界。然後它可以結束查詢,或者根據需要移至下一數據範圍。EXISTS的工作方式是查到匹配的記錄SQL Server就立即停止。使用索引所獲得的性能與使用EXISTS類似甚至更好,因為查找數據的過程的工作方式是類似的;也就是說,服務器可能使用某種索引知道何時沒有相關內容,並且立即停止。此外,可以對數據執行非常快速的查找(稱為SEEK),而不是在整個表中查找。

  3、索引類型和索引導航

  盡管表面上在SQL Server中有兩種索引結構(聚集索引和非聚集索引),但就內部而言,有3種不同的索引類型:①聚集索引,

②非聚集索引。

其中非聚集索引又包括以下兩種:①堆上的非聚集索引

②聚集表上的非聚集索引

  物理數據的存儲方式在聚集索引和非聚集索引中是不同的。而SQL Server遍歷平衡樹以到達末端數據的方式在所有3種索引類型中也是不同的。

  所有的SQL Server索引都有葉級和非葉級頁,葉級是保存標識記錄的“鍵”的級別,非葉級是葉級的引導者。

  索引在聚集表(如果表有聚集索引)或者堆(用於沒有聚集索引的表)上創建。

  (1)、聚集表

  聚集表是在其上具有聚集索引的任意表。但是它們對於表而言意味著以指定順序物理存儲數據。通過使用聚集索引鍵唯一地標誌獨立的行-聚集鍵即定義聚集索引的列。

  如果聚集索引不是唯一的,那將怎樣?如果索引不是唯一索引,那麽聚集索引如何用於唯一地標誌一行?SQL Server會在內部添加一個後綴到鍵上,以保證行具有唯一的標識符。

  (2)、堆

  堆是在其上沒有聚集索引的一個表。在這種情況下,基於行的區段、頁以及行偏移量(偏移頁頂部的位置)的組合創建唯一的標識符,或者稱為行ID(RID)。如果沒有可用的聚集鍵(沒有聚集索引),那麽RID是唯一必要的內容。堆表並不是B樹結構。

  4、聚集索引

  聚集索引對於任意給定的表而言是唯一的,一個表只能有一個聚集索引。不一定非要有聚集索引。聚集索引特殊的方面是:聚集索引的葉級是實際的數據-也就是說,數據重新排序,按照和聚集索引排序條件聲明的相同物理順序存儲。這意味著,一旦到達索引的葉級,就到達了數據。而非聚集索引,到達了葉級只是找到了數據的引用。

  任何新記錄都根據聚集列正確的物理順序插入到聚集索引中。創建新頁的方式隨需要插入記錄的位置而變化。如果新記錄需要插入到索引結構中間,就會發生正常的頁拆分。來自舊頁的後一半記錄被移到新頁,並且在適當的時候,將新記錄插入到新頁或舊頁。如果新記錄在邏輯上位於索引結構的末端,那麽創建新頁,但是只將新記錄添加到新頁。

  

  從數據插入的角度看,這裏應該能看到用int類型作為聚集索引的好處。

  為了說明索引是表的順序,請看一下表:

  

  然後在Id列建立聚集索引:

  CREATE CLUSTERED

  INDEX Index_Name ON Person(Id)  --建立Id列聚集索引

  執行查詢語句:

  select top 3 * from Person

  

  DROP INDEX Person.Index_Name --刪除索引

  CREATE CLUSTERED

  INDEX Index_Name ON Person(Name) --再在重建Name列聚集索引

  再執行查詢語句:

  select top 3 * from Person

  輸出結果如下:

  

  留意到同樣的語句,返回已經改變。可以聚集索引是表的順序,會影響到top語句。

  5、導航樹

  在SQL Server中甚至索引也是存儲在平衡樹中,在理論上,平衡樹在作為樹分支的每個可能方向上總是具有一般的剩余信息。聚集索引的平衡樹形式如下圖所示。

  

  在這裏,執行對數字158-400的範圍查詢(聚集索引非常擅長的事情),只需要導航到第一個記錄,並且包含在該頁上的所有剩余記錄。之所以知道需要該頁的剩余部分,是因為來自於上一級節點的信息也需要來自一些其他頁的數據。因為這是有序表,所以可以確信它是連續的-這意味著如果下一頁有符合條件的記錄,那麽這個頁的剩余部分必須被包含。無需任何驗證。

  首先導航到根節點。SQL Server能夠給予Sys.indexes系統元數據視圖中保存記錄項定位根節點。

  光說不練,純屬詐騙,下面以一個1萬行的PersonTenThousand表來說明B樹結構對數據頁讀取的提升。

  表的內容大致如下:

  

  一開始這張表並沒有任何索引:

  

  由於此表上沒有索引,因此只能夠通過堆表掃描獲得所需數據,因此,無論是檢索Id,還是Name列,都要整張表掃描一次。因此預讀,邏輯讀都要讀取所有的數據頁。

  下面在該表的Id列建立一個聚集索引:

  CREATE CLUSTERED INDEX index1 ON PersonTenThousand(ID)

  再來執行相同的查詢:

  

  我們看到,由於ID列是聚集索引,因此根據ID查找,B樹結構的優點就充分發揮了出來,只需要2次物理讀就能夠定位到數據。

  而Name列上沒有索引,因此還是需要預讀838次(還是聚集表掃描)才能定位到數據。

  以上例子充分說明了B-樹結構的優點。

  6、非聚集索引

  6.1 非聚集索引優點:

  1、因為在SQL Server中一頁只是8K,頁面空間有限,所以一行所包含的列數越少,它能保存的行就越多。非聚集索引通常不包含表中所有的列,它一般只包含非常少數的列。因此,一個頁上將能包含比表行(所有的列)更多行的非聚集索引。因此,同樣讀取一頁,在非聚集索引中可能包含200行,但是在表中可能只有10行,具體數據有表行的大小以及非聚集列的大小確定。

  2、非聚集索引的另一個好處是,它有一個獨立於數據表的結構,所以可以被放置在不同的文件組,使用不同的I/O路徑,這意味著SQL Server可以並行訪問索引和表,使查找更快速。

  下面說明一下,非聚集索引的好處:

  

  假設有一個單列的表,共有27行,每一頁上存了3行。沒有順序,假如我們要從中查找值為5的行,那麽需要的讀次數為9,因為它必須掃描到最後一頁,才能夠確定所有頁都不存在值為5的行了。

  假如建立了非聚集索引:

  

  再次查找值為5的行,那麽需要的讀次數為2,為什麽?因為非聚集索引是有順序的,當SQL Server讀取到值為6的那一行時,就知道不必再讀下去了。那麽如果要讀取值為25的頁呢?還是需要9個讀操作。因為它剛巧就在最後一頁。恰好這個東西,可以通過B樹結構來優化。B樹算法最小化了定位所需的鍵值訪問的頁面數量,從而加速了數據訪問過程。

  6.2 非聚集索引的開銷

  索引給性能帶來的好處有一定的代價。有索引的表需要更多的存儲和內存空間容納數據頁面之外的索引頁面。數據的增刪改可能會花費更長的時間,需要更多的處理時間以維護不斷變化的表的索引。如果一個INSERT語句添加一行到表中,那麽它也必須添加一行到索引結構中。如果索引是一個聚集索引,開銷可能會更大,因為行必須以正確的順序添加到數據頁面(當然分int聚集列和string聚集列會不同)。UPDATE和DELETE類似。

  雖然索引對增刪改有一定的影響,但是別忘了,要UPDATE或DELETE一行的前提是必須找到一行,因此索引實際上對於有復雜WHERE條件的UPDATE或DELETE也是有幫助的。在使用索引定位一行的有效性通常能彌補更新索引所帶來的額外開銷。除非索引設計不合理。

  7、堆上的非聚集索引

  在這裏要說明一點,無論是在堆上還是在聚集列上,非聚集索引都是排序後存儲的。按非聚集索引列排序。

  堆上的非聚集索引和聚集索引在大多數方面以類似的方式工具。其差別如下:

  葉級不是數據-相反,它是一個可從中獲得指向該數據的指針的級別。該指針以RID的形式出現(堆上一RID出現,聚集表上以聚集鍵出現),這種RID由索引指向的特定行的區段、頁以及行偏移量構成。即葉級不是實際的數據,使用葉級也僅僅比使用聚集索引多一個步驟。因為RID具有行的位置的全部信息,所以可以直接到達數據。

  差了一個步驟,實際上差別的系統開銷是很大的。

  使用聚集索引,數據在物理上是按照聚集索引的順序排列的。這意味著,對於一定範圍的數據,當找到在其上具有數據範圍起點的行時,那麽很可能有其他行在同一頁上(也就是說,因為他們存儲在一起,所以在物理上已幾乎到達下一個記錄)。

  使用堆,數據並未通過除索引外的其他方法連接在一起。從物理上看,絕對沒有任意種類的排序。這意味著從物理讀取的角度看,系統不得不從整個文件中檢索記錄。實際上,很可能最終多次從同樣的頁中取出數據。SQL Server沒有方法指導它將需要回到該物理位置,因為在數據之間沒有連接。因此,堆上的非聚集索引的工作方式是:通過掃描堆上的非聚集索引,找到(Row_Number行號),每找到一個RID,再通過RID取得數據。如果搜索是返回多個記錄,則性能可能比不上掃描全表。下圖顯示用堆上的非聚集索引執行與上面聚集索引相同的查詢:

  

  主要通過索引導航,一切都按以前的方式工作,以相同的根節點開始,,並且遍歷數,處理越來越集中的頁。直到到達索引的葉級。這裏有了區別。以聚集索引的方式,能夠正好在這裏停止,而以非聚集索引的方式,則需要做更多的工作。如果索引是在堆上,那麽只要在進入一個級別,獲得來自葉級頁的RID,並且定位到該RID-直到這時才可以直接獲得實際的數據。

  8、聚集表上的非聚集索引

  使用聚集表上的非集群索引時,還有一些類似性-但同樣也有區別。和堆上的非集群索引一樣,索引的非葉級及誒單的工作與使用聚集索引時幾乎一樣。區別出現在葉級。

  在葉級,與使用其他兩種索引結構所看到的內容有相當明顯的區別。聚集表上的非集群索引有另外一個索引來查找。使用聚集索引,當到達葉級時,可以找到實際的數據,當使用堆上的非集群索引,不能找到實際的數據,但是可以找到能夠直接獲得數據的標識符(僅僅多了一步)。使用聚集表上的非聚集索引,可以找到聚集鍵。也就是說,找到足夠的信息繼續並利用聚集索引。

  以上理解,說白了就是,當使用非聚集索引時,就是遍歷非聚集索引找到聚集索引,最後多次采用聚集索引找到數據。

  最終結果如下圖所示:

  

  首先是一個範圍搜索。在索引中執行一次單獨的查找,並且可以瀏覽非聚集索引以找到滿足條件(T%)的連續數據範圍。這種能夠直接到達索引中的特定位置的查找被稱為seek。

  然後第二個查找-使用聚集索引查找,第二種查找非常迅速:問題在於它必須執行多次。可以看到。SQL Server從第一個索引中查找檢索列表(所有名字以"T"開始的列表),但是該列表在邏輯上並沒有以任意連續的方式與聚集鍵相匹配-每個記錄單獨地查找。圖下圖所示:

  

  自然,這種多個查找的情況比一開始僅能使用聚集索引引入了更多的系統開銷。第一個索引查找-通過非聚集索引的方法-只需要非常少的邏輯讀操作。

  註意上圖,使用聚集表上的非聚集索引,找到的是一個聚集索引鍵的列表。然後用這個列表,逐個使用聚集索引查找到所需的數據。

  註意,如果表沒有聚集索引,建立了非聚集索引,那麽非聚集索引使用的是行號,如果此時你又添加了聚集索引,那麽所有的非聚集索引引用的RID都要改為聚集索引鍵。這對性能的消耗是非常大的,因此最好先建立聚集索引,在建立非聚集索引。

關於索引的幾個要點:

1、群集索引通常比非群集索引快(書簽)。

2、僅在將得到高級別選擇性的列(90%以上)上放置非群集索引。

3、所有的數據操作語言(DML:INSERT、UPDATE、DELETE、SELECT)語句可以通過索引獲益,但是插入、刪除和更新會因為索引而變慢。

4、索引會占用空間。

5、僅當索引中的第一列和查詢相關時才使用索引。

6、索引的負面影響和它的正面影響一樣多 - 因此只建立需要的索引。

7、索引可為非結構化XML數據提供結構化的數據性能,但是要記住,和其他索引一樣,會涉及到系統開銷。

數據庫存儲結構:頁、聚集索引、非聚集索引