天天看點

資料庫索引的實作原理—SQL Server表和索引的結構

索引使資料以一種特定的方式組織起來,進而可以提供對資料的快速通路。

表和索引的結構

頁和分區

頁是SQL Server存儲資料的基本機關,大小為8 KB。它可以包含表資料或索引資料,執行計劃資料,配置設定位圖,可用空間資訊等。頁是SQL Server可以讀寫的最小I/O機關。即使隻通路一行,它也把整個頁加載到緩存并從緩存中讀取資料。涉及資料查詢操作的開銷通常是I/O開銷。顯然,實體地讀取一頁比從緩存中邏輯地讀取一頁所産生的開銷要大得多。

區是8個連續頁組成的配置設定單元。當表或索引需要更多空間以存儲資料時,SQL Server為該對象配置設定一個完整的區。但有一個例外:如果該對象不足64 KB(8*8 KB(頁)),則當需要更多空間時,SQL Server通常隻為該對象配置設定一個完整的頁,而不是整個區。該頁可以位于一個混合區内,混合區内的8個頁屬于不同的對象。

I/O操作中最大開銷是磁臂的移動,而實際的磁讀和磁寫操作的開銷要小得多,是以讀取一個頁幾乎和讀取整個區所用的時間一樣。

堆是不含聚集索引的表。這種結構之是以被稱為堆是因為它的資料不按任何順序存儲。

聯系一個堆中的資料的唯一結構是被稱為索引配置設定映射(Index Allocation Map ,IAM)的一個位圖頁(如果需要,可能是多個IAM頁)。該位圖頁包含指向在混合區(mixed extent)配置設定的前八個頁的指針,還包含一個大位圖,每個位表示檔案中4 G範圍内的一個區,如果一個區不屬于擁有該IAM頁的對象,則标記為0,否則為1。如果一個IAM不足以覆寫該對象的所有資料,SQL Server将維護一個IAM頁的鍊。當掃描對象時,使用IAM頁周遊該對象的資料。SQL Server加載該對象的第一個IAM頁,然後訓示磁臂按區在磁盤上的實體順序連續地通路他們。

SQL Server維護指向第一個IAM頁和堆中第一個資料頁的内部指針。

資料庫索引的實作原理—SQL Server表和索引的結構

聚集索引

SQL Server中所有索引都是按B+樹的結構來組織的。

聚集索引是按B+樹的結構來組織的,聚集索引在葉級表中維護所有的資料。

資料庫索引的實作原理—SQL Server表和索引的結構

我們把具有聚集索引的表稱為聚集表。表的完整資料行按索引鍵的順序存儲在索引的葉級。有一個雙向連結清單用于維護這種邏輯順序。但要注意,根據索引的碎片級别,磁盤上頁的實體順序可能和連結清單維護的邏輯順序不比對。

還要注意,對于每個葉級行(元組),索引維護一個唯一辨別符(unq)。該值列舉具有相同鍵值(key value)的行,當索引的鍵列不唯一時,該值和鍵值共同唯一的辨別行。

當SQL Server需要在索引的葉級别執行有序掃描(或有序局部掃描)操作時,将按連結清單進行掃描。除了連結清單,SQL Server還維護一個(或多個)IAM頁以映射在磁盤上按實體順序存儲的資料。當SQL Server需要無序掃描索引的葉級别時,通常使用IAM頁。有序和無序掃描的性能差異取決于索引的碎片級别。I/0操作中成本最高的部分是磁臂的移動。在沒有碎片的索引中執行有序掃描與無序掃描的性能相差無幾,但如果索引的碎片級别較高,則有序掃描要慢得多。

在索引葉級别的上層,索引還維護其他的級别,每個級别都概括了它下面的級别。非葉級索引頁中的每一行指向它下一級别的整個頁。該行包括兩個元素:1、被指向索引頁的第一行的鍵值;2、以及一個6位元組的指針,該指針包括資料庫的檔案号和檔案中的頁号。當SQL Server生成索引時,它從葉級别開始,向上添加級别。當一個級别隻有一個頁時停止,而該頁被稱為根頁(root page)。

當SQL Server需要導航到位于葉級的特定鍵時,它總是從根頁開始,并使用被稱為索引查找(index seek)的通路方法。查找操作将從根“跳”到下一級别的相應頁,并繼續從一個級别跳到下一個級别,直到達到包含被查找鍵的葉級頁。所有葉級頁到達根的距離都是相同的,這意味着一次查找操作在頁讀取(page reads)方面的成本正好是索引的級數。這種讀取操作的I/O模式不是連續的I/0,而是随機的I/O,因為查找操作所讀取的頁很少相鄰。

在性能估計時,知道一個索引的級數是非常重要的,因為這個數正好是一次查找操作在頁讀取方面的成本。一般而言,對于小表,大多數索引通常隻有兩級。對于大表(百萬級),通常包含3級或4級。相對于聚集索引,非聚集索在它的葉級頁可以容納更多的行,是以相同級數的非聚集索引可以包含更多的行,因為非聚集索引的葉級行僅包含索引鍵列和指向特定資料行的行定位符。

非聚集索引

非聚集索引也是按B+樹的結構來組織的,而且在許多方面都和聚集索引類似。唯一差別是非聚集索引的葉級行僅包含索引鍵列和指向特定資料行的行定位符。行定位符的内容取決于該表是一個堆還是一個聚集表。

堆上的非聚集索引

非聚集索引的葉級行中指向資料行的行定位符是一個8位元組的實體指針,被稱為RID。它由資料庫檔案号,檔案中的目标頁号,目标頁中的行号(從0開始)組成。當通過索引查找到特定行時,SQL Server必須在查找操作後執行RID lookup操作,該操作用于讀取包含資料行的頁。是以,RID lookup的成本是一個頁讀取。

對于一次lookup或者少量lookup,它的成本并不高,但是對于大量的lookup來說,它的成本會非常高,因為SQL Server需要為找到的每個行讀取頁。對于使用非聚集索引的範圍查詢,以及一批lookup(每次符合條件的行一次lookup),lookup操作的累計成本通常構成了查詢的大部分成本。

資料庫索引的實作原理—SQL Server表和索引的結構

聚集表上的非聚集索引

與堆上的非聚集索引唯一的差別是:在聚集表上建立的非聚集索引的行定位符是一個被稱為聚集鍵的值,而不是RID。聚集鍵由目标行的聚集索引值和唯一辨別符組成。其原理是指向邏輯的行,而不是實體的行。

這種架構主要是為OLTP(聯機事務處理)系統而設計的,在這種系統下,當插入資料時聚集索引常常出現大量的頁拆分。當被拆分的頁中,有一半的行會被實體地移動到新配置設定的頁。如果非聚集索引儲存指向行的實體位址,所有指向這些被移動資料行的指針必須被更改以反映新的實體位置,而且對于所有非聚集索引中的相關指針也都要被更新。如果SQL Server維護邏輯指針,則實體地移動資料行時,非聚集索引上的這些指針不需要被更新。

查找操作在非聚集索引中查找特定鍵,最後到達相應的葉級行并通路行定位符。這種情況下的行定位符是被指向行的聚集鍵,lookup操作将在聚集索引内根據得到的聚集鍵執行完整的查找操作。其中每次lookup操作的成本(讀取頁數方面)與聚集索引的級數一樣高。而當表是一個堆時,RID lookup的成本隻是一個頁讀取(page read)。當然,對于使用非聚集索引的範圍查詢和一系列lookup,在堆和聚集索引表中邏輯讀取數之間的比例接近于L,其中L是聚集索引的級數。

在你對此感到困惑并移除所有聚集索引之前,記住,對聚集索引執行lookup操作時,聚集索引的非葉級頁通常都已經在緩存中了。通常,大多數聚集索引的實體讀取是發生在葉級的。是以對聚集表的額外lookup成本相對于堆來說隻占總查詢成本的一小部分。

資料庫索引的實作原理—SQL Server表和索引的結構

繼續閱讀