SQL Server 查詢優化 索引的結構與分類

一、索引的結構

關係型數據庫中以二維表來表達關係模型,表中的數據以頁的形式存儲在磁盤上,在SQL SERVER中,數據頁是磁盤上8k的連續空間,那麼,一個表的所有數據頁在磁盤上是如何組織的呢?分兩種情況:一是數據頁間無序、隨機地存儲在磁盤上,這樣的表叫做堆表;二是數據頁間按某個表字段的值有序地存儲在磁盤上,這樣的表做索引組織表。

 

索引是什麼?從物理結構上可分爲兩種:聚集索引和非聚集索引。將表中的數據有序地組織起來的索引稱爲聚集索引,一個表只有一個聚集索引,表上其他的索引都是非聚集索引。

 

1.1、聚集索引結構

(1)聚集索引將表內的數據進行有序的組織,並不是指磁盤上數據頁內數據的物理順序,也不是指數據頁在磁盤上的物理順序,而是數據頁間邏輯上以樹型結構鏈接起來;

create table t

(

ID       int,

NAME     varchar(100)

AGE      int,

)

GO

INSERT INTO t VALUES(1,'張一',20)

INSERT INTO t VALUES(2,'張二',25)

INSERT INTO t VALUES(4,'張三',21)

INSERT INTO t VALUES(5,'李二',23)

INSERT INTO t VALUES(7,'李三',24)

INSERT INTO t VALUES(8,'李四',22)

GO

create CLUSTERED index IX_t_ID on t(ID)

 (

2)聚集索引的非葉子節點(即索引節點)行中,只包含下一節點的第一個鍵值及指向下一節點的指針,指針的格式爲:文件編號+頁編號,長度爲2Byte+4Byte=6Byte;

(3)聚集索引的葉子節點行就是表中的數據行;

(4)沒有聚集索引的表,結構如下:

堆表中的數據頁沒有經過組織,隨機的存放在磁盤上,通過IAM頁進行管理,可以知道哪些數據頁屬於某個表以及數據頁的分配情況,對於數據頁的結構及數據庫引擎對其管理,這裏不多作介紹,詳見《SQL Server 存儲引擎》系列。

(5)數據庫引擎根據系統目錄判斷當前表是否爲索引組織表,以選擇索引組織表的root_page或堆表的first_IAM_page及first_page,即可對錶進行掃描;

(6)索引組織表和堆表包含的數據行是一樣的,只是組織形式不同而已;

 

1.2、非聚集索引結構

(1)非聚集索引是對聚集索引的索引;

(2)非聚集索引的索引節點行和聚集索引一樣,只包含下一節點的第一個鍵值及指向下一節點的指針,指針的格式爲:文件編號+頁編號,長度爲2Byte+4Byte=6Byte;

(3)非聚集索引的葉子節點行存儲的是索引列和書籤。如果是索引組織表,書籤爲聚集索引鍵;如果是堆表,書籤爲ROWID,長度爲8Byte,即數據頁號(4Byte)+文件號(2Byte)+槽號(2Byte)的行定位串;

create NONCLUSTERED index IX_t_AGE on t(AGE)

GO

SELECT * FROM t WHERE AGE=20

此時想要根據年齡20來查找數據。

如果是索引組織表,先根據AGE上的非聚集索引找到ID,此時ID值爲1,然後再將ID=1帶入聚集索引進行等值查找,最終在聚集索引的葉子節點得到該行的所有數據;

如果是堆表,先根據AGE找的應該是ID=1這一行數據的8字節的ROWID,然後再根據這個ROWID去找到該行的所有數據,即(1,'張一',20);

(4)無論是聚集索引還是非聚集索引的葉子節點上都有一個指向上下頁的指針。

 

二、索引分類

1、SQL SERVER中索引分類

(1)B+樹索引

目前關係型數據庫中一種常見的索引組織結構。B+樹,它是一多叉平衡排序樹,直到葉子節點纔會命中數據,以下簡稱B樹,可參見相關《數據結構》的書籍;

 

(2)、全文索引

目前關係型數據庫一種基於標記的索引組織結構,它不是B樹結構,而是基於要索引的文本中的各個標記來創建倒排、堆積且壓縮的索引結構。

 

(3)、XML索引

隨着XML文本的應用,在各個關係型數據庫中也相繼提供了對這種數據結構的支持。XML 實例作爲二進制大型對象 (BLOB) 存儲在 xml 類型列中。對於列中的每個 XML對象,索引將創建幾個數據行。該索引中的行數大約等於 XML對象中的節點數。

 

1.2、B樹索引的分類

1.2.1、物理結構分類

(1)聚集索引

根據索引列值,按B樹結構對錶內數據進行組織;

 

(2)非聚集索引

對聚集索引的索引;

 

1.2.2、列值唯一性分類

(1)唯一索引

表中索引列的值唯一。

SQL SERVER在唯一性上認爲NULL是相等的(ORACLE中唯一鍵是可以插入多個NULL值的),即唯一鍵中只允許出現一個NULL值,但在比較運算中認爲NULL是不相等的,這點要注意;

 

(2)非唯一索引

表中索引列的值不唯一;

 

1.2.3、列個數分類

(1)複合索引

包含多個列的索引;

 

(2)單列索引

只包含一個列的索引。

 

1.2.4、特殊索引

(1)計算列索引

計算列上的索引;

 

(2)視圖索引

對視圖建立索引。

一、遍歷

索引樹的每個節點都是一個頁面。

索引樹有三種類型的節點:根節點、中間節點、葉子節點。

根節點與中間節點一樣,只包含下一層節點的入口值與入口指針,它們稱爲索引節點;

葉子節點包含要遍歷的數據,對聚集索引而言數據就是表中數據行,對非聚集索引數據是指索引列值和行書籤。

 

索引的遍歷總是從根節點開始,即先根遍歷,分爲兩種:索引掃描和索引查找。

索引掃描是指從索引樹的根節點開始,對葉子節點逐個掃描,直至命中所有滿足查找條件的數據;

索引查找是指從索引樹的根節點開始,按查找值在索引節點中根據路由信息跳轉,直至葉子節點以命中數據。

 

B+樹的深度通常小於等於3,計算如下:

以聚集索引爲例,簡單計算如下:10個INT列寬度總和爲40B,假設聚集索引樹每一層爲二叉,共三層,即2^0+2^1+2^2=1*(1-2^3)/(1-2)=7個頁面,4個葉子節點,每個頁面8060K可存儲8060000/40=201500行,乘以4=806000行,如果是三叉、四叉,那麼三層可存儲上千萬至億行的數據,當然在數據量達到這個等級時,通常我們會選擇表分區,那麼B樹深度就更不會突破三層了。

所以索引查找的效率是很高的,在查詢中應該努力構造索引查找,避免索引掃描。

 

二、插入

2.1、頁空間充足

在已存在數據的表上,創建或重建索引時,可指定填充因子,即在索引樹的每個節點上預留一定的空間,供表中後續增加的數據使用。但如果在創建表的時候就創建了索引,並指定了填充因子,這時的填充因子是無用的,數據庫系統不會刻意去保留頁面的空間。

 

索引頁面有剩餘空間的情況如下圖:

 


 圖
1

參考圖1,此時向索引樹中插入一條索引鍵值爲31的記錄,步驟如下:

(1)執行索引鍵值=31的查找操作,確定該新記錄應該插入到葉子節點L2中。

(2)檢查L2上是否有足夠的空間來存放當前記錄,這裏假設有足夠的空間;

(3)將記錄45向後移動,插入索引鍵值爲31的新記錄。插入之後,10、30、31、45還是順序的,如下圖:

圖2

 2.2、頁空間不足

 參加圖2,此時再插入一條索引鍵值爲32的記錄,步驟如下:

(1)執行索引鍵值=32的查找操作,確定該新記錄應該插入到葉子節點L2中;

(2)檢查L2上是否有足夠的空間來存放當前記錄,這時發現沒有足夠的頁空間,此時需要進行頁面分裂;

(3)向數據庫系統申請一個新的頁面L4,將L2的一半數據移到L4中,並重新鏈接葉子的左右節點,如下圖:

圖3

(4)此時,上層節點也需要生成一個新的葉子節點的指針。這裏的上層節點即根節點,如果上層節點沒有剩餘空間的話,同樣也需要進行分裂,這裏有剩餘空間,如下圖:

圖4

(5)因爲當前記錄的鍵值範圍位於頁分裂的後一半中,將索引鍵值爲32的新記錄插入到L4中,如果鍵值範圍位於前一半,則插入到L2中。如果L4的空間不夠存放鍵值爲32的新記錄,則L4會繼續進行頁分裂,這裏假設空間足夠,插入結束,如下圖:

圖5

三、刪除

3.1、刪除葉子節點中的記錄

參考圖5,刪除索引鍵值爲32的記錄,步驟如下:

(1)執行索引鍵值=32的查找操作,確定該記錄在L4中;

(2)將索引鍵值=32的記錄標記爲虛影,但並不立即釋放空間,虛影記錄可用於事務回滾、多版本等;

(3)如果此時L4上的虛影記錄空間被申請使用,虛影記錄就會被擦除;

(4)如果數據頁面最後一條記錄也被刪除,數據頁面會被回收;

 

3.2、刪除非葉子節點中的記錄

(1)索引節點中的指針被刪除時並不是虛影記錄,但同樣也不釋放空間,直到有新的指針插入時,纔會進行空間壓縮;

(2)堆表中數據行被刪除後,頁空間不會被回收,即使是空閒分頁也還是標識爲分配狀態,無法被其他對象使用;

 

注:從理論上講,在兄弟節點頁面空閒空間都小於50%時,應該將兄弟節點合併,即分裂的逆操作,但這樣可能帶來的後果是更頻繁的頁面合併、分裂,成本更大,所以在數據庫系統中通常不進行頁面合併操作。

 

四.更新

4.1、覆蓋更新

如果更新操作能夠在頁內進行原位鍵值替換,那麼就進行覆蓋更新。

 

4.2、非覆蓋更新

無法進行覆蓋更新時,更新操作被分解爲刪除和插入操作。

 

如果非覆蓋更新過程中,新的記錄比較長,則會在頁面分裂的過程中會帶來數據行的移動:

(1)聚集索引的移動對非聚集索引沒有影響,因爲非聚集索引中存儲的是聚集索引的鍵值,分裂並不會改變鍵值;

(2)堆表中的數據頁分裂,會在原記錄處留下一個前轉指針,以告訴非聚集索引去哪裏找新的記錄;

所以數據行的移動對非聚集索引都不會帶來維護的成本,非聚集索引的維護成本來自書籤的變化:

(1)聚集索引的鍵值發生變化或被刪除;

(2)堆表中的數據行被刪除。

一、索引的作用

1、幫助檢索數據;

2、提高聯接效率;

3、節省ORDER BY、GROUP BY的時間;

4、保證數據唯一性(僅限於唯一索引)。

 

二、索引的設計

在確定要建立一個索引時,首先我們要確定它是聚集還是非聚集、單列還是多列、唯一還是非唯一、列是升序還是降序、它的存儲是如何的,比如:分區、填充因子等。下面逐條來看:

1、聚集索引

(1)首先指出一個誤區,主鍵並不一定是聚集索引,只是在SQL SERVER中,未明確指出的情況下,默認將主鍵定義爲聚集,而ORACLE中則默認是非聚集,因爲SQL SERVER中的ROWID未開放使用。

(2)聚集索引適合用於需要進行範圍查找的列,因爲聚集索引的葉子節點存放的是有序的數據行,查詢引擎可根據WHERE中給出的範圍,直接定位到兩端的葉子節點,將這部分節點頁的數據根據鏈表順序取出即可;

(3)聚集索引儘量建立在值不會發生變更的列上,否則會帶來非聚集索引的維護;

(4)儘量在建立非聚集索引之前建立聚集索引,否則會導致表上所有非聚集索引的重建;

(5)聚集索引應該避免建立在數值單調的列上,否則可能會造成IO的競爭,以及B樹的不平衡,從而導致數據庫系統頻繁的維護B樹的平衡性。聚集索引的列值最好能夠在表中均勻分佈。

 

2、非聚焦索引

(1)非聚集索引適合用於需要進行等值查找的列,因爲非聚集索引的葉子節點存放的是有序的索引列與書籤的映射行,查詢引擎可根據WHERE中給出的值,得到書籤,繼而定位到數據行;

(2)覆蓋索引(Covering Index),是非聚集索引的一種特殊且高效的應用,就是將需要返回的數據列設計成組合索引,在SELECT時只查詢索引中存在的數據列,這樣就能形成索引覆蓋,因爲索引行中已經包含了想到的數據,不需要再進行書籤查找;

在SQL SERVER 2005及以上版本中,提供了INCLUDED關鍵字,可以在非聚集索引中包含更多列,也是覆蓋索引的一個有效引申;

(3)非聚集索引建立在值具有單調性的列上,比如:自增列(單調遞增),可以減少索引的外部碎片及索引結構的維護;

 

3、複合索引

(1)複合索引建立在多個列上。上面已經講過,在非聚集索引中,可以利用覆蓋索引來提高檢索的效率,但如果組合索引的列太多的話,那麼對於這個索引的維護成本也會加大,DML的效率將會下降,而且索引的查找路徑會變長;

(2)在創建複合索引時,應該將高選擇性的列放在前面,即作爲引導列;

 

4、唯一索引

(1)再指出一個誤區,聚集索引並不一定是唯一索引,由於SQL SERVER將主鍵默認定義爲聚集索引,事實上,索引是否唯一與是否聚集是不相關的,聚集索引可以是唯一索引,也可以是非唯一索引;

(2)將索引設置爲唯一,對於等值查找是很有利的,當查到第一條符合條件的紀錄時即可停止查找,返回數據,而非唯一索引則要繼續查找,同樣,由於需要保證唯一性,每一行數據的插入都會去檢查重複性;

 

5、分區索引

(1)SQL SERVER從2005引入的分區表的概念,對於在分區表上建立的索引,無論是否包含分區列,在未指定分區方案或文件組的情況下,均會使用分區表的分區方案來創建該索引。這一點與ORACLE正好相反;

 

6、其他索引

(1)篩選索引,在ORACLE中叫域索引,這是SQL SERVER 2008新增的功能,適用於表中數據密度不高的列來創建索引,比如:表中某列大多是NULL或某相同值,那麼此時可通過WHERE關鍵字來篩選這些相同值來創建索引,這樣在對非相同的值進行檢索時,即可以用到這個索引;

(2)計算列索引,在ORACLE中叫函數索引,如果表中存在計算列,但想要對計算列進行檢索,可在計算列上創建索引,但前提是必須要先持久化該計算列;

(3)索引視圖,在ORACLE中叫物化視圖,我們知道視圖的數據都是來源於基礎表,在SQL SERVER中對視圖建立索引,事實上就對視圖進行了物化,然後才創建索引,索引視圖多用於靜態數據的查詢,畢竟物化視圖的更新是比較麻煩的;

(4)位圖索引,對BOOL型字段建立索引,在SQL SERVER中暫未提供這樣的功能;

(5)XML索引,對XML列只能創建XML索引;

(6)全文索引,LOB類型列上無法建立普通索引,比如VARCHAR(MAX)、TEXT、IMAGE,如果需要檢索可以考慮建立全文索引,當然全文索引也可以用在非LOB類型的列上;

 

7、索引參數

(1)填充因子,索引的葉子節點的填充程序,預留一定的頁空間,以避免過多的頁拆分,但如果預留空間太大,會放大查詢的成本。對於索引的中間節點如果也使用填充因子可以打開PAD_INDEX選項;

(2)忽略重複鍵,這個選項通常用於去重處理,對於將要插入到表中的行,如果存在相同索引鍵,則拋棄;

(3)根據業務查詢需要,決定列升序還是降序,通常對於單列索引存在正向或反向掃描,但在複合索引的非引導列中檢索時,如果存在需要倒排的查詢,則在建立索引時應選擇降序;

(4)在索引的存儲上,將聚集和非聚集索引定義在不同磁盤分區的文件組上,減少IO競爭,亦可利用多CPU併發,但目前通常都是RAID5+1(1備份5中的一塊硬盤)的存儲,數據存放本就已分散,這樣做的意義並不是太大。

 

8、索引列選擇

(1)參考[索引的作用]選擇索引列,比如:如果表中存在外鍵,考慮在該列上建立索引,可增加參照完整性檢查的速率,同樣,如果存在連接,也可提高連接的速率;

(2)索引選擇性公式:selectivity=unique keys/rows,一般當選擇性低於0.1即10%時,查詢優化器拒絕使用該索引,比如:BOOL類型的列。此處有一個矛盾,有可能我們無法知道當前列的selectivity,那麼只能採用估算的方式,比如採用SQL SERVER 2005的TABLESAMPLE進行數據採樣等;

(3)在面對一個多條件的查詢時,應該選擇過濾性較強的字段作爲單列索引,或作爲複合索引的引導列;

(4)在長度較小的數據類型上建立索引,這樣每條索引存儲所佔用的空間就會變小,每個索引頁上就能夠存儲更多的索引行,縮短索引查找的路徑;

(5)不要在一個表上建立太多的索引,索引的維護會影響DML的效率;

 

三、索引的使用

1、避免無效搜索表達式

(1)WHERE條件中將索引列放在表達式中:如fun(c1) = 1,c1+c2 = 2等;

(2)索引列在未知數值起點的LIKE運算符中:如Like ‘%abc’或like ‘%abc%’;

(3)包含否定運算符的表達式,如:<>、not in/exists/like;

(4)NULL表達式,如:is null、is not null;

總之,無法讓檢索值與索引值,進行比較運算的表達式,都爲無效搜索表達式。

 

2、複合索引的引導列

比如複合索引列爲(c1,c2,c3),則在WHERE條件中必須指定c1,纔會用到該複合索引;

 

以上兩點總的原則就是:構造索引查找,避免索引掃描,我們通常所說的:能不能用到索引,也應該是指索引查找,而不是索引掃描。

 

3、儘量使用等值運算符

比如在使用WHERE c1>1時,SQL SERVER會盡量去將它轉會爲WHERE c1>=2(如果整形的話),以給索引查找一個起點,如果無法隱式轉換爲>=,則會以c1>=1爲起點,查找完成後再踢除=1的數據;

 

4、表達式的順序

在WHERE條件中包含多個表達式時,應該將過濾性最強的表達式放在靠近WHERE的位置,通常這樣的表達式中的列,也會選擇在上面建立索引,因爲在一個WHERE子句中只有一個表達式是可以使用索引的,其他的表達式都是基於索引查找結果集的過濾,所以應儘量保證有效索引的使用。儘管查詢優化器會去識別,然後自動去重排表達式的順序,但我們應該養成一個好的習慣。

 

一、索引碎片

無論是索引組織表(IOT)還是堆表(HEAP),隨着數據的增刪改,都會或多或多的產生碎片。碎片的存在,主要對於數據掃描效率有着較大的影響,對於數據查找效率幾乎沒有影響或者說影響很小,如果想要改善數據查找的效率,進行索引碎片整理並沒有什麼效果。以下的碎片分類也主要是從數據掃描着眼。

1、內部碎片

內部指的是頁內,即頁面的空閒空間。其實填充因子就是一種碎片,爲了減少頁拆分,寧願適當地去製造這種碎片。但在大量內部碎片一直處於無法被數據填充的情況下,是沒有益處的,它會導致掃描過程中讀取額外的頁面。

對於LOB和ROW_OVERFLOW_DATA頁面,這是唯一的碎片形式,因爲在這兩種列上無法建立B樹索引。

 

2、外部碎片

(1)邏輯碎片

索引葉子節點頁的邏輯順序與物理順序不一致,比如:有頁號1,2的兩個頁面,此時1頁面發生頁拆分,這時新申請的頁面頁號爲3,此時邏輯順序爲1-3-2,但物理順序是1-2-3,1頁面沒有直接指向磁盤的下一個物理頁,這就造成了不一致,即邏輯碎片;

 

(2)擴展碎片

SQL SERVER通常給表或索引分配新的空間是以EXTENT(區或擴展)的形式,一個區是8個頁面,所以區的第一個頁號應該是8的倍數,比如:一個包含有序區的表,第一個頁面的頁號應該是8-16-24,這樣下去,如果是8-24,那麼說明第一個頁面頁號爲16的區被分配給了另一個表,那麼8-24的表在物理上就存在一個間隙,即擴展碎片;

 

外部碎片是對數據連續性的度量,擴展碎片是堆表數據連續性的度量,數據的連續性越差,掃描的成本也會越大。

 

二、查看與管理

對於索引樹的管理主要考慮兩方面:一是B樹的平衡性,這一點數據庫系統會自動維護;二是索引碎片,這需要手動去維護。

1、碎片的查看

SQL SERVER支持兩種碎片查看方式,至於這兩種碎片查看方式的使用方法,幫助文檔裏有很詳細的說明。

(1)dbcc showcontig是SQL SERVER 2000中的唯一碎片查看方式,在SQL SERVER 2005中無法支持LOB類型、ROW_OVERFLOW_DATA及整個分區表的碎片查看;

 

(2)sys.dm_db_index_physical_stats是SQL SERVER 2005新的碎片查看方式;

 

對於碎片的檢測以及是否需要進行碎片處理,主要從以下幾個參數來看:

(1)內部碎片檢測

avg_page_space_used_in_percent:頁面空間平均使用比例;

對於數據掃描而言,該參數越大越好,這意味着讀取較少的頁面即可返回想要的數據。

 

fragment_count:IN_ROW_DATA碎片的數量;

avg_fragment_size_in_pages:IN_ROW_DATA碎片的平均頁大小;

以上參數反應了行內數據頁碎片的數量及碎片的大小,即便碎片數量很多,但如果碎片很大即很整塊的話,通常要大於64KB即一個EXTENT,對於數據掃描而言,效率也是很高的,因爲SQL SERVER會跳過這些整塊的碎片。

 

ghost_record_count和version_ghost_record_count:虛影紀錄數;

對於數據掃描而言,該參數越小越好。

 

forwarded_record_count:前轉紀錄數;

前轉紀錄只會在堆中存在,對於數據掃描而言,是非常有幫助的。

 

(2)外部碎片檢測

avg_fragmentation_in_percent:頁面平均外部碎片的比例;

對於數據掃描而言,該參數越小越好。

 

2、碎片的整理

(1)、索引重建

顧名思義,重新建立索引,對索引的數據進行重新排列。在SQL SERVER 2000中這是一個脫機操作,即索引重建完成前,索引無法訪問或使用,在SQL SERVER 2005/2008中支持聯機操作。

 

聯機過程的實現,就是先維護另一份新索引,然後同步舊索引中變化的部分,同步完成後使用架構鎖鎖定舊索引,切換表到新的索引上,再釋放架構鎖,切換的過程是數據庫系統目錄的維護過程,速度很快,所以可以理解爲是聯機操作。

 

索引重建的方式有四種:

A:drop index再create index

這種方法是最差的,它會導致表上非聚集索引的兩次重建,一次在DROP時指向ROWID,一次在CREATE時重新指向聚集鍵。以下三種方法只需重新非聚集索引一次。

而且對於主鍵約束或唯一鍵約束產生的索引無法直接刪除,必須要先刪除約束,纔可以刪除索引;

 

B:create index的drop_existing選項

這種方法完全重建索引,並可以重新指定索引參數;

 

C:alter index的rebuild選項

這是SQL SERVER 2005/2008新的重建索引的方式,它不會重建非聚集索引,除非指定ALL關鍵字,同時它也更靈活,可以針對表上某個分區重建索引;

 

D:dbcc dbreindex;

這是SQL SERVER 2000時重建索引的方法,它默認使用表上原來create index的參數重建索引。填充因子可重新指定。

 

(2)、索引重組

將索引樹的葉節點頁重新排序,以消除外部碎片。這是一個聯機操作。但不同於索引重建的是,索引重組後統計信息不會得到更新,而且索引重組使用的是冒泡排序法,效率比較低。同時,索引重組只能在單個文件內進行,無法跨文件重組索引。

 

索引重組的方式有兩種:

A:alter index的reorgnize選項

這是SQL SERVER 2005/2008新的重組索引的方式,和dbcc indexdefrag一樣,它默認對錶上所有分區進行索引重組,不可以重新指定填充因子,因爲只是索引葉子節點的重新排序。但它比dbcc indexdefrag的選項更豐富些;

 

B:dbcc indexdefrag

這是SQL SERVER 2000時重組索引的方法。

 

注意:

(1)碎片是不可避免,但並不是說一旦檢測到碎片就進行碎片整理,有時少量碎片的整理工作反而會帶來更大的成本消耗,所以在不能確定索引整理能否帶來良好性能時,不要輕易進行索引整理。

(2)不管是內部碎片還是外部碎片,即便進行了碎片整理,也並不一定能夠完全消除,對於外部碎片,可以將表獨立存放在一個文件組上,並在文件初始化時分配足夠的磁盤空間,這樣的表經過碎片整理後外部碎片可以消除,但太過浪費磁盤空間;

(3)無論是索引重建、索引重組都會對數據頁進行壓縮,頁面的填充程度由填充因子決定。它們都是事務操作,會產生日誌,爲了減少事務日誌的大小,可在索引整理後進行日誌截斷或日誌備份。

(4)如有必要,對於DML非常頻繁的表,爲了保證數據掃描的效率,可以考慮利用作業來在系統相對空閒的時候定期進行索引整理

 

物理結構的設計,也即是設計數據在磁盤上的存儲,需要考慮:如何做到安全、IO競爭少、服務器資源利用率高。

 

通常當前數據庫只使用一份ONLINE的數據源,至於分佈式存儲已經上升到服務器架構的設計,這裏不作討論。以下僅簡單描述非分佈式存儲的情況,主要從磁盤、文件、表數據三方面來說。

一、磁盤

RAID0提供了最好的讀寫性能,但RAID0沒有提供冗餘恢復策略,所以很少單獨全用。通常,使用RAID10或RAID5來實現磁盤數據的存儲與容災。

 

隨着磁盤上數據量的提升,RAID5的讀寫性能會明顯低於RAID10,RAID5至少需要3塊磁盤,RAID10至少需要4塊磁盤,RAID5在同樣提供數據容災的情況下,更能節省成本,但如果成本不在考慮範圍內,當然是性能優先,使用RAID10。

 

二、文件

2.1、文件組(表空間)

在SQL SERVER中叫文件組,在ORACLE中叫表空間,它們都是對數據文件的邏輯分組。使用文件組的目的如下:

(1)性能

文件組中的文件放在不同磁盤上,是否可以提高讀寫的效率?在使用RAID1或RAID5的情況下,這種效率的提升已經幾乎可以忽略,因爲數據本身已經被分散寫到RAID上的各塊磁盤上;

 

(2)管理

目前使用文件組的目的,大都出於管理。

a)在磁盤空間不足的情況下,可以使用多個小空間的磁盤,同時,也可爲數據庫擴展新的磁盤空間;

b)備份/還原,只讀/讀寫,離線/在線等數據庫管理與維護;

 

SQL SERVER中文件組與文件的關係如下圖:


 

 



  

2.2、文件

(1)如果文件組中的文件被限制大小,文件填滿了會不會自動新建一個文件?

不會,那麼此時數據庫會報出空間不足的錯誤,所以在定義文件時,要麼指定其大小無限增長(直到填滿磁盤空間),要麼定期檢查文件空閒空間的大小,以手動擴展。

 

(2)文件組中文件是一個個寫入還是各個文件同時都在增大?

SQL SERVER中是文件級別中各個文件同時寫入,ORACLE中是寫滿一個再寫下一個。

 

三、表數據

3.1、堆表和索引組織表

使用堆表還是索引組織表,取決於對DML效率的要求。

(1)堆表在DML時沒有聚集索引的維護成本,在非明確指定的情況下,ORACLE中默認使用堆表;

(2)對於主鍵的單值或範圍查找,尤其是範圍查找,索引組織表效率更高,因爲省去了bookmark lookup的成本,在非明確指定的情況下,SQL SERVER中默認使用索引組織表。

 

關於堆表和索引組織表的設計,詳見《SQL Server 查詢優化》系列。

 

3.2、表數據分割

數據分割的方式,通常可分爲以下兩種:

3.2.1、水平分割

出於讀寫性能的考慮,將一個大表,拆分爲若干個小的單元,應用程序根據每個單元的入口規則,映射到相應的單元,只處理所需訪問的單元中的數據,以提高數據讀寫的效率。

 

水平分割針對表中數據行進行分割,可採用以下方式:

(1)分表

在還沒出現分區的概念時,對於大表,通常採用分表的方式來實現數據的水平分割。

 

按照一定的規則將數據分別存入不同名稱的表中:

a)最常見的就是按日期、時間來分表,比如:將每月的訂單放到當前月的訂單表中,orders_201001、orders_201002、orders_201003……依次類推;

 

b)也可以根據編號規則來分表,比如將尾數:將訂單尾數爲1的訂單放到一個表中,orders_1、orders_2、orders_3……依次類推;

 

(2)表分區

a)SQL SERVER 2005以前的版本中,出現過分區視圖,算是它在分區表出現前的一個替代品,有本地分區視圖和分佈式分區視圖兩種。分佈式分區視圖的概念,倒是有現在分佈式存儲的味道,但由於受網絡因素影響較大,未被廣泛使用。

 

b)對於本地化的表分區,目前多采用分區表,結構如下圖:

 

3.2.2、垂直分割

垂直分割針對表中列進行分割。

如果表中列非常多,從而導致一行數據所佔用的存儲空間非常大時,可以考慮進行垂直分割,比如:將一個表拆成主表和從表,主表中存放訪問最頻繁的列,從表中存放訪問較少的列,從而提高數據讀取的效率。

 

通常不建議使用垂直分區,如果主從表都變得很大,在需要進行主從表關聯時,成本也是相當高的,可能會影響性能。

 

3.3、簇

簇(CLUSTER),這是ORACLE裏的一個概念,將多個表中相同的字段存放到一起,稱爲簇,即聚集的意思,這裏需要和聚集索引的概念區分開來。放到一起的好處如下:

(1)節省存儲空間,相同數據只需要存一份;

(2)提高多表關聯的速度;


發佈了20 篇原創文章 · 獲贊 9 · 訪問量 16萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章