1.1.1 摘要
如果說要對數據庫進行優化,我們主要可以通過以下五種方法,對數據庫系統進行優化。
1. 計算機硬件調優
2. 應用程序調優
3. 數據庫索引優化
4. SQL語句優化
5. 事務處理調優
在本篇博文中,我們將想大家講述數據庫中索引類型和使用場合,本文以SQL Server為例,對于其他技術平臺的朋友也是有參考價值的,只要替換相對應的代碼就行了!
索引使數據庫引擎執行速度更快,有針對性的數據檢索,而不是簡單地整表掃描(Full table scan)。
為了使用有效的索引,我們必須對索引的構成有所了解,而且我們知道在數據表中添加索引必然需要創建和維護索引表,所以我們要全局地衡量添加索引是否能提高數據庫系統的查詢性能。
在物理層面上,數據庫有數據文件組成,而這些數據文件可以組成文件組,然后存儲在磁盤上。每個文件包含許多區,每個區的大小為64K由八個物理上連續的頁組成(一個頁8K),我們知道頁是SQL Server數據庫中的數據存儲的基本單位。為數據庫中的數據文件(.mdf 或 .ndf)分配的磁盤空間可以從邏輯上劃分成頁(從0到n連續編號)。
頁中存儲的類型有:數據,索引和溢出。
文件和文件組
在SQL Server中,通過文件組這個邏輯對象對存放數據的文件進行管理。
1.1.2 正文
在物理層面上,數據庫有數據文件組成,而這些數據文件可以組成文件組,然后存儲在磁盤上。每個文件包含許多區,每個區的大小為64K由八個物理上連續的頁組成(一個頁8K),我們知道頁是SQL Server數據庫中的數據存儲的基本單位。為數據庫中的數據文件(.mdf 或 .ndf)分配的磁盤空間可以從邏輯上劃分成頁(從0到n連續編號)。
頁中存儲的類型有:數據,索引和溢出。
文件和文件組
在SQL Server中,通過文件組這個邏輯對象對存放數據的文件進行管理。
![sqlserver 索引的一些總結 sqlserver 索引的一些總結]()
圖2文件組成
頁中保存的數據類型有:表數據、索引數據、溢出數據、分配映射、頁空閑空間、索引分配等,具體如下圖所示:
頁類型
內容
Data
當 text in row 設置為 ON 時,包含除 text、 ntext、image、nvarchar(max)、varchar(max)、varbinary(max) 和 xml 數據之外的所有數據的數據行。
Index
索引條目。
Text/Image
大型對象數據類型:text 、 ntext、image、nvarchar(max)、varchar(max)、varbinary(max) 和 xml 數據。數據行超過 8 KB 時為可變長度數據類型列:varchar 、nvarchar、varbinary 和 sql_variant
Global Allocation Map、Shared Global Allocation Map
有關區是否分配的信息。
Page Free Space
有關頁分配和頁的可用空間的信息。
Index Allocation Map
有關每個分配單元中表或索引所使用的區的信息。
Bulk Changed Map
有關每個分配單元中自最后一條 BACKUP LOG 語句之后的大容量操作所修改的區的信息。
Differential Changed Map
有關每個分配單元中自最后一條 BACKUP DATABASE 語句之后更改的區的信息。
表1頁中保存的數據類型
在數據頁上,數據行緊接著頁頭(標頭)按順序放置;頁頭包含標識值,如頁碼或對象數據的對象ID;數據行持有實際的數據;最后,頁的末尾是行偏移表,對于頁中的每一行,每個行偏移表都包含一個條目,每個條目記錄對應行的第一個字節與頁頭的距離,行偏移表中的條目的順序與頁中行的順序相反。
![sqlserver 索引的一些總結 sqlserver 索引的一些總結]()
圖4索引的葉節點和相應的表數據
如上圖4所示,索引葉節點包含索引值和相應的RID(ROWID),而且葉節點通過雙向鏈表有序地連接起來;同時我們主要到數據表不同于索引葉節點,表中的數據無序存儲,它們不全是存儲在同一表塊中,而且塊之間不存在連接。
總的來說,索引保存著具體數據的物理地址值。
索引的類型
我們知道索引的類型有兩種:聚集索引和非聚集索引。
聚集索引:物理存儲按照索引排序。
非聚集索引:物理存儲不按照索引排序。
聚集索引
聚集索引的數據頁是物理有序地存儲,數據頁是聚集索引的葉節點,數據頁之間通過雙向鏈表的形式連接起來,而且實際的數據都存儲在數據頁中。當我們給表添加索引后,表中的數據將根據索引進行排序。
假設我們有一個表T_Pet,它包含四個字段分別是:animal,name,sex和age,而且使用animal作為索引列,具體SQL代碼如下:
復制代碼 代碼如下:
-----------------------------------------------------------
---- Create T_Pet table in tempdb.
-----------------------------------------------------------
USE tempdb
CREATE TABLE T_Pet
(
animal VARCHAR(20),
[name] VARCHAR(20),
sex CHAR(1),
age INT
)
CREATE UNIQUE CLUSTERED INDEX T_PetonAnimal1_ClterIdx ON T_Pet (animal)
-----------------------------------------------------------
---- Insert data into data table.
-----------------------------------------------------------
復制代碼 代碼如下:
DECLARE @i int
SET @i=0
WHILE(@i<1000000)
BEGIN
INSERT INTO T_Pet (
animal,
[name],
sex,
age
)
SELECT [dbo].random_string(11) animal,
[dbo].random_string(11) [name],
'F' sex,
cast(floor(rand()*5) as int) age
SET @i=@i+1
END
INSERT INTO T_Pet VALUES('Aardark', 'Hello', 'F', 1)
INSERT INTO T_Pet VALUES('Cat', 'Kitty', 'F', 2)
INSERT INTO T_Pet VALUES('Horse', 'Ma', 'F', 1)
INSERT INTO T_Pet VALUES('Turtles', 'SiSi', 'F', 4)
INSERT INTO T_Pet VALUES('Dog', 'Tomma', 'F', 2)
INSERT INTO T_Pet VALUES('Donkey', 'YoYo', 'F', 3)
![sqlserver 索引的一些總結 sqlserver 索引的一些總結]()
SET STATISTICS PROFILE OFF
SET STATISTICS TIME OFF
當我們執行完SQL查詢計劃時,把鼠標指針放到“聚集索引查找”上,這時會出現如下圖信息,我們可以查看到一個重要的信息Logical Operation――Clustered Index Seek,SQL查詢是直接根據聚集索引獲取記錄,查詢速度最快。
![sqlserver 索引的一些總結 sqlserver 索引的一些總結]()
圖7查詢結果
現在我們把表中的索引刪除,重新執行查詢計劃,這時我們可以發現Logical Operation已經變為Table Scan,由于表中有100萬行數據,這時查詢速度就相當緩慢。
![sqlserver 索引的一些總結 sqlserver 索引的一些總結]()
圖9查詢結果
通過上面的有聚集索引和沒有的對比,我們發現了查詢性能的差異,如果使用索引數據庫首先查找索引,而不是漫無目的的全表遍歷。
非聚集索引
在沒有聚集索引的情況下,表中的數據頁是通過堆(Heap)形式進行存儲,堆是不含聚集索引的表;SQL Server中的堆存儲是把新的數據行存儲到最后一個頁中。
非聚集索引是物理存儲不按照索引排序,非聚集索引的葉節點(Index leaf pages)包含著指向具體數據行的指針或聚集索引,數據頁之間沒有連接是相對獨立的頁。
假設我們有一個表T_Pet,它包含四個字段分別是:animal,name,sex和age,而且使用animal作為非索引列,具體SQL代碼如下:
復制代碼 代碼如下:
-----------------------------------------------------------
---- Create T_Pet table in tempdb with NONCLUSTERED INDEX.
-----------------------------------------------------------
USE tempdb
CREATE TABLE T_Pet
(
animal VARCHAR(20),
[name] VARCHAR(20),
sex CHAR(1),
age INT
)
CREATE UNIQUE NONCLUSTERED INDEX T_PetonAnimal1_NonClterIdx ON T_Pet (animal)
![sqlserver 索引的一些總結 sqlserver 索引的一些總結]()
SET STATISTICS PROFILE OFF
SET STATISTICS TIME OFF
如下圖所示,我們發現查詢計劃的最右邊有兩個步驟:RID和索引查找。由于這兩種查找方式相對于聚集索引查找要慢(Clustered Index Seek)。
![sqlserver 索引的一些總結 sqlserver 索引的一些總結]()
圖11查詢計劃
首先SQL Server查找索引值,然后根據RID查找數據行,直到找到符合查詢條件的結果。
查詢執行時間:CPU 時間= 0 毫秒,占用時間= 1 毫秒
![sqlserver 索引的一些總結 sqlserver 索引的一些總結]()
圖13堆表非聚集索引
通過上圖,我們發現非聚集索引通過雙向鏈表連接,而且葉節點包含指向具體數據行的指針。
如果我們要查找animal = ‘Dog'的信息,首先我們遍歷第一層索引,然后數據庫判斷Dog屬于Cat范圍的索引,接著遍歷第二層索引,然后找到Dog索引獲取其中的保存的指針信息,根據指針信息獲取相應數據頁中的數據,接下來我們將通過具體的例子說明。
現在我們創建表employees,然后給該表添加堆表非聚集索引,具體SQL代碼如下:
復制代碼 代碼如下: