a8din}0ITPUB个人空间Z @Xp p4G I
那么到底什么是索引碎片呢?索引碎片实际上有2种形式:外部碎片和内部碎片。不管哪种碎片基本上都会影响索引内页的使用。这也许是因为页的逻辑顺序错误(即外部碎片)或每页存储的数据量少于数据页的容量(内部错误)。无论索引产生了哪种类型的碎片,你都会因为它而面临查询的性能问题。ITPUB个人空间f b:tqn? ]o
ITPUB个人空间;cb(`5CQ
外部碎片ITPUB个人空间Mm3[6Xy(e5m
X/W1q'S$H&d0当索引页不在逻辑顺序上时就会产生外部碎片。索引创建时,索引键按照逻辑顺序放在一组索引页上。当新数据插入索引时,新的键可能放在存在的键之间。为了让新的键按照正确的顺序插入,可能会创建新的索引页来存储需要移动的那些存在的键。这些新的索引页通常物理上不会和那些被移动的键原来所在的页相邻。创建新页的过程会引起索引页偏离逻辑顺序。ITPUB个人空间P#SUM w
ITPUB个人空间([!HQ!y1JW5l
下面的例子将比实际的言论更加清晰的解释这个概念。
_c;WQ5S n}0假定在任何另外的数据插入你的表之前存在索引上的结构如下ITPUB个人空间-H4d^.bZ
;K-YQ6vw(c b0(注:下面图片里应该是7和8,原文里是6和8):
(c z(s^YRp0

7l:V?z+Mm"IBH0
-C:XP}/qUX6A u+a0INSERT语句往索引里添加新的数据,假定添加的是5。INSERT将引起新页创建,为了给5在原来的页上留出空间,7和8被移到了新页上。这个创建将引起索引页偏离逻辑顺序。ITPUB个人空间ilFlZ+?2a O

ITPUB个人空间_w#n+uZ!PJ
在有特定搜索或者返回无序结果集的查询的情况下,偏离顺序的索引页不会引起问题。对于返回有序结果集的查询,搜索那些无序的索引页需要进行额外的处理。有序结果集的例子如查询返回4到10之间的记录。为了返回7和8,查询不得不进行额外的页切换。虽然一个额外的页切换在一个长时间运行里是无关紧要的,然而想象一下一个有好几百页偏离顺序的非常大的表的情形。
xmUX)L%Q0ITPUB个人空间yN!|%TgEX
内部碎片
W5d W(q]#~V9Dp0ITPUB个人空间.@Xo9[]
当索引页没有用到最大量时就产生了内部碎片。虽然在一个有频繁数据插入的应用程序里这也许有帮助,然而设置一个fill factor(填充因子)会在索引页上留下空间,服务器内部碎片会导致索引尺寸增加,从而在返回需要的数据时要执行额外的读操作。这些额外的读操作会降低查询的性能。
J&kRWH g0ITPUB个人空间T`o6a?.F6yq"?
怎样确定索引是否有碎片?
vG!rri0ITPUB个人空间Z/P.?'AI9Atu)S
SQLServer提供了一个数据库命令――DBCC SHOWCONTIG――来确定一个指定的表或索引是否有碎片。ITPUB个人空间;d{ d5?M
DBCC SHOWCONTIGITPUB个人空间O*t8tJ:rx
数据库平台命令,用来显示指定的表的数据和索引的碎片信息。ITPUB个人空间z sn9M#ff!jY)`O
B9L^})L*jn0DBCC SHOWCONTIG权限默认授予sysadmin固定服务器角色或db_owner和db_ddladmin固定数据库角色的成员以及表的所有者且不可转让。
#}]V%koEk-sd5z0语法(SQLServer2000)
xo wZwn9d0ITPUB个人空间2h9S+R%P N U%[+f9^B
DBCC SHOWCONTIG
0bt:])cO0[ ( { table_name | table_id| view_name | view_id }
3x Svd7i-XZP9}"R0[ , index_name | index_id ]
ig2UB q2_ K:s0)
L`u]*pR0]
O8oQ&p)jMg4xy[]0[ WITH { ALL_INDEXESITPUB个人空间 WI)s2\'h^t
| FAST [ , ALL_INDEXES ]
HICZ!uV }8H0| TABLERESULTS [ , { ALL_INDEXES } ]
.Nc Q^$b0[ , { FAST | ALL_LEVELS } ]
*Xxg7P\OG0}ITPUB个人空间nC0i2U j"Wh C
]
+TNi2fL].W0
J}4M|.Wo'Xs0语法(SQLServer7.0)ITPUB个人空间9_7`-@Xh
p z#[7\ ydV5z5B0DBCC SHOWCONTIGITPUB个人空间"l r1~#N)rA
[ ( table_id [,index_id ]
'Y7EN7n"j:Yk0)ITPUB个人空间 baV%Yd[dY
]
'J2@!{$s!A}t~1m0ITPUB个人空间K0m5FzYNu y
ITPUB个人空间(I4{3D_!TZ5Gcy2~ z
示例:ITPUB个人空间f:A`'?5qyZ
显示数据库里所有索引的碎片信息ITPUB个人空间8tA Vd2B\,c?/C
SET NOCOUNT ON
Y1?ZwvZ1nC\0USE pubs
;{G/wdd*O1elFU0DBCC SHOWCONTIG WITH ALL_INDEXES
k7x"L#M8chl0
n,qdt8r^m`0
GO
U_1c9E/t$[I!u0显示指定表的所有索引的碎片信息ITPUB个人空间6?/vsL*x^:_p:YM
SET NOCOUNT ONUSE pubs
.I ~ jac"?z(B;C0DBCC SHOWCONTIG (authors) WITH ALL_INDEXESITPUB个人空间!c(?8S D3Ocg ^
GO
ITPUB个人空间Xm1NG*yYu
显示指定索引的碎片信息ITPUB个人空间@9x^`I(hk~m0k
SET NOCOUNT ON
$y"\f-Wi4l0USE pubsITPUB个人空间U J T$ad1{
DBCC SHOWCONTIG (authors,aunmind)ITPUB个人空间9K{%E{,QB5JwK
GO
ITPUB个人空间.U5e4M)XOo
结果集ITPUB个人空间8C,t'o~ s X`B\
DBCC SHOWCONTIG将返回扫描页数、扫描扩展盘区数、遍历索引或表的页时,DBCC语句从一个扩展盘区移动到其它扩展盘区的次数、每个扩展盘区的页数、扫描密度(最佳值是指在一切都连续地链接的情况下,扩展盘区更改的理想数目)。
^ A!YI_E*d,S(`s0
@TVQ[8h^.FD0DBCC SHOWCONTIG正在扫描'authors'表...
+hDP L8D$`"PFr0表: 'authors'(1977058079);
)Bpc\%nl@0索引ID: 1,数据库ID: 5
:PGz-y+U.~/Q~5?0已执行TABLE级别的扫描。
f7Xt0P,dv8nP*{0-扫描页数.....................................: 1
d"}$IK7MVI0-扫描扩展盘区数...............................: 1
G*@,C3F/m0-扩展盘区开关数...............................: 0ITPUB个人空间(|r:O&eheJ
{c
-每个扩展盘区上的平均页数.....................: 1.0ITPUB个人空间 m5P1fj f6u
-扫描密度[最佳值:实际值]....................:
100.00%[1:1]
/OM)CT9Y"@&Q0-逻辑扫描碎片.................................: 0.00%
q9lC |#HX P0-扩展盘区扫描碎片.............................: 0.00%
TLXqqTw0-每页上的平均可用字节数.......................: 6010.0ITPUB个人空间]aDLb8K
-平均页密度(完整)...........................: 25.75%ITPUB个人空间)q1M&go.E"t
DBCC执行完毕。如果DBCC输出了错误信息,请与系统管理员联系。
5`,E"D/C$B s r y `:E0寻找什么ITPUB个人空间:kM"I ~Fn2}/lp.N)A
GFuSSo/R0扫描页数:如果你知道行的近似尺寸和表或索引里的行数,那么你可以估计出索引里的页数。看看扫描页数,如果明显比你估计的页数要高,说明存在内部碎片。
b|!l emz1o0
Ylq IuB \)t0扫描扩展盘区数:用扫描页数除以8,四舍五入到下一个最高值。该值应该和DBCC SHOWCONTIG返回的扫描扩展盘区数一致。如果DBCC SHOWCONTIG返回的数高,说明存在外部碎片。碎片的严重程度依赖于刚才显示的值比估计值高多少。
{'CE!mZDg0
`T$V'h3O:p0扩展盘区开关数:该数应该等于扫描扩展盘区数减1。高了则说明有外部碎片。ITPUB个人空间oi1`YG+N,R
8nK)fsP0G{s] }.B0每个扩展盘区上的平均页数:该数是扫描页数除以扫描扩展盘区数,一般是8。小于8说明有外部碎片。
/D&?KV)Dy0
1g'v7ov?l(M0扫描密度[最佳值:实际值]:DBCC SHOWCONTIG返回最有用的一个百分比。这是扩展盘区的最佳值和实际值的比率。该百分比应该尽可能靠近100%。低了则说明有外部碎片。
}+vx5]gP!L*N@0
7r\BG.X0逻辑扫描碎片:无序页的百分比。该百分比应该在0%到10%之间,高了则说明有外部碎片。ITPUB个人空间3@(dd_ ^5E%X
ITPUB个人空间L o5j7e&F*R5r
扩展盘区扫描碎片:无序扩展盘区在扫描索引叶级页中所占的百分比。该百分比应该是0%,高了则说明有外部碎片。ITPUB个人空间;FL:Ah/`"ol
ITPUB个人空间 KSG}E3xJD
每页上的平均可用字节数:所扫描的页上的平均可用字节数。越高说明有内部碎片,不过在你用这个数字决定是否有内部碎片之前,应该考虑fill factor(填充因子)。
1I6\zC c/u/I.f0ITPUB个人空间 rbv#\1O}
平均页密度(完整):每页上的平均可用字节数的百分比的相反数。低的百分比说明有内部碎片。
0|^k f^fM0ITPUB个人空间:`*^[ \R.t'oN fa
备注ITPUB个人空间W*yb7j(qIgRc*r
U$F \MB5R`)D Gu0DBCC SHOWCONTIG实际上仅对那些大表有用。小表显示的结果根本不符合正常标准,因为他们也许没有由多于8个的页面组成。你在查看小表上执行DBCC SHOWCONTIG的结果时应该忽略一些结果。在处理小表时只需关心扩展盘区开关数、逻辑扫描碎片、每页上的平均可用字节数、平均页密度(完整)。ITPUB个人空间:[)gm!]n$|ip
ITPUB个人空间NWyy8F K(V
DBCC SHOWCONTIG默认输出的结果是:扫描页数、扫描扩展盘区数、扩展盘区开关数、每个扩展盘区上的平均页数、扫描密度[最佳值:实际值]、逻辑扫描碎片、扩展盘区扫描碎片、每页上的平均可用字节数、平均页密度(完整)。可以用FAST和TABLERESULTS选项来控制这个输出结果。ITPUB个人空间 CTdJ$ahWw6U
ITPUB个人空间8mYqa*`d8t4p.E
FAST选项指定执行索引的快速扫描,输出结果是最小的,该选项不读索引的叶或数据页且只返回扫描页数、扫描扩展盘区数、扫描密度[最佳值:实际值]、逻辑扫描碎片。
:WR@ Z0iI1C0fE_j0ITPUB个人空间?P d4?!BC'tm
TABLERESULTS选项将用行集的形式显示信息,将返回扩展盘区开关数、扫描密度[最佳值:实际值]、逻辑扫描碎片、扩展盘区扫描碎片、每页上的平均可用字节数、平均页密度(完整)。ITPUB个人空间SBj RB O{9S([ p)I
5?VYea0N+LlY7R)U x0如果既指定FAST选项又指定TABLERESULTS选项,那么将返回对象名、对象ID、索引名、索引ID,页数、扩展盘区开关数、扫描密度[最佳值:实际值]和逻辑扫描碎片。ITPUB个人空间&J'c f4I-Ch6u5D rC\/R
(h K pls0dD QV0ALL_INDEXES选项将显示指定表和试图的所有索引的结果,即使指定了一个索引。ITPUB个人空间@A G9Y?W4e!J
T |K4{ @,yrwm K${0ALL_LEVELS选项指定是否为所处理的每个索引的每个级别产生输出(默认只输出索引的页级或表数据级的结果),并且只能与TABLERESULTS选项一起使用。ITPUB个人空间 hDSiqg
ITPUB个人空间9g3O#O;A:Lq-kzya8P
解决碎片问题
#jy$fUPS8k'Z x0
?+~4N X%y0一旦你确定表或索引有碎片问题,那么你有4个选择去解决那些问题:ITPUB个人空间.x*_9Mjb6^
- 删除并重建索引
- 使用DROP_EXISTING子句重建索引
- 执行DBCC DBREINDEX
- 执行DBCC INDEXDEFRAG
尽管每一个技术都能达到你整理索引碎片的最终目的,但各有各的优缺点。
+Y6Bni8?.[,Xc_1v!G0ITPUB个人空间H1i?M6~
删除并重建索引
xXsi:kgV]$m0
gl`$II^6J;wI0用DROP INDEX和CREATE
INDEX或ALTER TABLE来删除并重建索引有些缺陷包括在删除重建期间索引会消失。在索引删除重建时,对于查询它不在可用,查询性能也许会受到明显的影响,直到重建索引为止。另一个潜在的缺陷是当都请求索引的时候会引起阻塞,直到重建索引为止。通过其他的处理也能解决阻塞,就是索引被使用的时候不删除索引。另一个主要的缺陷是在用DROP
INDEX和CREATE INDEX重建聚集索引时会引起非聚集索引重建两次。删除聚集索引时非聚集索引的行指针会指向数据堆,聚集索引重建时非聚集索引的行指针又会指回聚集索引的行位置。
Z7x'Od'z/F)x|0
-~7Ja0Z9uS9Oq0删除并重建索引的确有一个好处就是通过重新排序索引页,使索引页紧凑并删除不需要的索引页来完全重建索引。你也许需要考虑那些内部和外部碎片都很高的情况下才使用,以使那些索引回到它们应该在的位置。ITPUB个人空间F!aC,s I-X6syv
ITPUB个人空间2EA~xl+sR
使用DROP_EXISTING子句重建索引ITPUB个人空间/i J{*aE.Yl
ITPUB个人空间K1cgi:QK%|d~j
为了避免在重建聚集索引时表上的非聚集索引重建两次,可以使用带DROP_EXISTING子句的CREATE
INDEX语句。这个子句会保留聚集索引键值,以避免非聚集索引重建两次。和删除并重建索引一样,该方法也可能会引起阻塞和索引消失的问题。该方法的另一个缺陷是也强迫你去分别发现和修复表上的每一个索引。ITPUB个人空间zz1W!w'`'n9P:l_*^
2bE%{Xg1\l0除了和上一个方法一样的好处之外,该方法的好处是不必重建非聚集索引两次。这样可以对那些带约束的索引提供正确的索引定义以符合约束的要求。
4Q$D8Lhy}7E8P,JE0ITPUB个人空间eE\}3KTi Z
执行DBCC DBREINDEXITPUB个人空间$~(z&_mCHt4a]&j
ITPUB个人空间 @nHu2~'q6cU
DBCC DBREINDEX类似于第二种方法,但它物理地重建索引,允许SQLServer给索引分配新页来减少内部和外部碎片。DBCC
DBREINDEX也能动态的重建带约束的索引,不象第二种方法。ITPUB个人空间 l-Ia3~,K U ie} N ~
ITPUB个人空间Y2?-wy*o|"F9B)m2}
DBCC DBREINDEX的缺陷是会遇到或引起阻塞问题。DBCC DBREINDEX是作为一个事务来运行的,所以如果在完成之前中断了,那么你会丢失所有已经执行过的碎片。ITPUB个人空间Fa%}r9S
Ht9q
ITPUB个人空间5H:]s'k s`@%b
执行DBCC INDEXDEFRAGITPUB个人空间0AN4W1P G
)s7z.A LL;g1f0DBCC INDEXDEFRAG(在SQLServer2000中可用)按照索引键的逻辑顺序,通过重新整理索引里存在的叶页来减少外部碎片,通过压缩索引页里的行然后删除那些由此产生的不需要的页来减少内部碎片。它不会遇到阻塞问题但它的结果没有其他几个方法彻底。这是因为DBCC
INDEXDEFRAG跳过了锁定的页且不使用任何新页来重新排序索引。如果索引的碎片数量大的话你也许会发现DBCC INDEXDEFRAG比重建索引花费的时间更长。DBCC
INDEXDEFRAG比其他方法的确有好处的是在其他过程访问索引时也能进行碎片整理,不会引起其他方法的阻塞问题。