SQL Server聚集索引的选择
先聲明文章非原創(chuàng),摘自博客園:http://www.cnblogs.com/CareySon/archive/2012/03/06/2381582.html
簡介
?? 在SQL Server中,數(shù)據(jù)是按頁進(jìn)行存放的。而為表加上聚集索引后,SQL Server對于數(shù)據(jù)的查找就是按照聚集索引的列作為關(guān)鍵字進(jìn)行了。因此對于聚集索引的選擇對性能的影響就變的十分重要了。本文旨在從性能角度來談聚集索引的選擇,但這僅僅是從性能方面考慮。對于有特殊業(yè)務(wù)要求的表,則需要按實(shí)際情況進(jìn)行選擇。
一、聚集索引所在列或列的組合最好是唯一的
?? 這個原因需要從數(shù)據(jù)的存放原理來談。在SQL Server中,數(shù)據(jù)的存放方式并不是以行(Row)為單位,而是以頁為單位。因此,在查找數(shù)據(jù)時(shí),SQL Server查找的最小單位實(shí)際上是頁。也就是說即使你只查找一行很小的數(shù)據(jù),SQL Server也會將整個頁查找出來,放在緩沖池中。
? 每一個頁的大小是8K。每個頁都會有一個對于SQL Server來說的物理地址。這個地址的寫法就是文件號:頁號(理解文件號需要你對文件和文件組有所理解)。比如第一個文件的第50頁。則頁號為:1:50。當(dāng)表沒有聚集索引時(shí),表中的數(shù)據(jù)頁是以堆(Heap)進(jìn)行存放的,在頁的基礎(chǔ)上,SQL Server通過一個額外的行號來確定每一行,這也是傳說中的RID。RID是文件號:頁號:行號來進(jìn)行表示的,假設(shè)這一行在起前面所說的頁中的第5行,則RID表示為1:50:5,如圖1所示:
? 從RID的概念來看,RID不僅僅是SQL Server唯一確定每一行的數(shù)據(jù),也是存放行的存放位置。當(dāng)頁通過堆(Heap)進(jìn)行組織時(shí),頁很少進(jìn)行移動。
? 而當(dāng)表上建立索引時(shí),表中的頁按照B樹進(jìn)行組織。此時(shí),SQL Server尋找行不再是按RID進(jìn)行查找,轉(zhuǎn)而使用了關(guān)鍵字,也就是聚集索引的列作為關(guān)鍵字進(jìn)行查找。假設(shè)圖1的表中,我們設(shè)置DepartmentID列作為聚集索引列。則B樹的非葉子節(jié)點(diǎn)的行中只包含了DepartmentID和指向下一層結(jié)點(diǎn)的書簽(BookMark)。
? 而當(dāng)我們創(chuàng)建的聚集索引的值不唯一時(shí),SQL Server則無法僅僅通過聚集索引列(也就是關(guān)鍵字)唯一確定一行。此時(shí),為了實(shí)現(xiàn)對每一行的唯一區(qū)分,則需要SQL Server為相同值的聚集索引生成一個額外的標(biāo)示信息進(jìn)行區(qū)分,這也是所謂的uniquifiers。而使用了uniquifier后,對性能產(chǎn)生的影響分為如下部分:
1、SQL Server必須在插入或者更新時(shí)對現(xiàn)在的數(shù)據(jù)進(jìn)行判讀是否和現(xiàn)有的鍵重復(fù),如果重復(fù),則需要生成uniqifier,這個是一筆額外開銷。
2、因?yàn)樾枰獙ο嗤档逆I添加額外的uniquifier來區(qū)分,因此鍵的大小被額外的增加了。因此無論是葉子節(jié)點(diǎn)和非葉子節(jié)點(diǎn),都需要更多的頁進(jìn)行存儲。從而還影響到非聚集索引,使得非聚集索引的書簽列變大,從而使得非聚集索引也需要進(jìn)行更多的頁進(jìn)行存儲。
下面我們進(jìn)行測試,創(chuàng)建一個測試表,創(chuàng)建聚集索引。插入10W條測試,其中每2條一重復(fù),如圖2所示。
--創(chuàng)建測試表 create table [dbo].[TestP] ([id] int,[Name] varchar(100) ) go--在id上創(chuàng)建聚集索引 create clustered index testp_cindex on TestP(id) go --插入10W條數(shù)據(jù)測試,每2條一重復(fù) begin tran declare @index int set @index=0 while(@index<100000) begin insert into dbo.TestP(id,Name)values(@index,'測試數(shù)據(jù)')insert into dbo.TestP(id,Name)values(@index,'測試數(shù)據(jù)') set @index=@index+1 end commitexec sp_spaceused 'TestP'我們插入了測試數(shù)據(jù)
此時(shí),我們來查看這個表所占的頁數(shù),如圖3所示。
插入重復(fù)鍵后10W數(shù)據(jù)占了359頁
我們再次插入10W不重復(fù)的數(shù)據(jù),如圖所示
??? 此時(shí),所占頁數(shù)縮減為335頁,如圖5所示。
???????? 因此,推薦聚集索引所在列使用唯一鍵。
二、最好使用窄列或窄列組合作為聚集索引列
??? 這個道理和上面減少頁的原理一樣,窄列使得鍵的大小變小。使得聚集索引的非葉子節(jié)點(diǎn)減少,而非聚集索引的書簽變小,從而葉子節(jié)點(diǎn)頁變得更少。最終提高了性能。
?
三、使用值很少變動的列或列的組合作為聚集索引列
??? 在前面我們知道。當(dāng)為表創(chuàng)建聚集索引后。SQL Server按照鍵查找行。因?yàn)樵贐數(shù)中,數(shù)據(jù)是有序的,所以當(dāng)聚集索引鍵發(fā)生改變時(shí),不僅僅需要改變值本身,還需要改變這個鍵所在行的位置(RID),因此有可能使得行從一頁移動到另一頁。從而達(dá)到有序。因此會帶來如下問題:
- ??? 行從一頁移動到另一頁,這個操作是需要開銷的,不僅如此,這個操作還可能影響到其他行,使得其他行也需要移動位置,有可能產(chǎn)生分頁
- ??? 行在頁之間的移動會產(chǎn)生索引碎片
- ??? 鍵的改變會影響到非聚集索引,使得非聚集索引的書簽也需要改變,這又是一筆額外的開銷
???? 這也就是為什么很多表創(chuàng)建一列與數(shù)據(jù)本身無關(guān)的列作為主鍵比如AdventureWorks數(shù)據(jù)庫中的Person.Address表,使用AddressID這個和數(shù)據(jù)本身無關(guān)的列作為聚集索引列,如圖6所示。而使用AddressLine1作為主鍵的話,員工地址的變動則可能造成上面列表的問題。
???
??? 圖6.創(chuàng)建和數(shù)據(jù)本身無關(guān)的一列作為聚集索引列
?
四、最好使用自增列作為聚集索引列
??? 這個建議也同樣推薦創(chuàng)建一個和數(shù)據(jù)本身無關(guān)的自增列作為聚集索引列。我們知道,如果新添加進(jìn)來的數(shù)據(jù)如果聚集索引列需要插入當(dāng)前有序的B樹中,則需要移動其它的行來給新插入的行騰出位置。因此可能會造成分頁和索引碎片。同樣的,還會造成修改非聚集索引的額外負(fù)擔(dān)。而使用自增列,新行的插入則會大大的減少分頁和碎片。
?? 最近我碰到過一個情況。一個表每隔幾個月性能就奇慢無比,初步查看是由于有大量的索引碎片。可是每隔幾個月重建一次索引讓我無比厭煩。最終我發(fā)現(xiàn),問題是由于當(dāng)時(shí)設(shè)計(jì)數(shù)據(jù)庫的人員將聚集索引建在了GUID上,而GUID是隨機(jī)生成的,則可能插入到表的任何位置,從而大大增加了碎片的數(shù)量。因此造成上面這種情況。
?
總結(jié)
??? 本文簡單介紹了SQL Server存儲的原理和應(yīng)該規(guī)避的幾種聚集索引建立情況,但這僅僅是從性能的角度來談聚集索引的選擇。對于聚集索引的選擇,還是需要全面的考慮進(jìn)行決定。
?
?
?
轉(zhuǎn)載于:https://www.cnblogs.com/zhijianliutang/archive/2012/05/16/2505552.html
總結(jié)
以上是生活随笔為你收集整理的SQL Server聚集索引的选择的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Debian下IPv6设定主地址 Set
- 下一篇: 数据库备份失败问题