信息交流、传播、提炼

nice to meet you

博客园 首页 新随笔 联系 订阅 管理

首先介绍些索引碎片相关概念、及检查和整理的方法:


索引碎片两种类型


一 外部碎片

产生的原因是因为索引不按照的逻辑顺序排列

比如 现在的索引页分配顺序是

第一页 第二页 第三页

数据:2 4 6 8 10 12 14 16 18 20 22 24

当我们插入新的数据比如5,系统可能就会这样分配,产生一个新的索引页

第一页 第二页 第三页 第四页

数据: 2 4 5 10 12 14 16 18 20 22 24 6 8

这时如果我们要查询4-10的数据,就需要一个额外的页来返回6,8两个数据

二 内部索引碎片

产生的原因是因为索引页没有充分利用到所分配的空间,内部索引碎片会导致增加索引空间

三 我们可以使用DBCC SHOWCONTIG来检查索引碎片

用法:


DBCC SHOWCONTIG --详细用法参看sqlserver联机文档


示例:

declare @table_id int
set @table_id=object_id('TA066')
dbcc showcontig(@table_id)

四 碎片整理(重建索引)

逻辑扫描碎片和扩展盘区扫描碎片都非常大,需要对索引碎片进行处理


一般有两种方法解决,一是利用DBCC INDEXDEFRAG整理索引碎片,二是利用DBCC DBREINDEX重建索引。二者各有优缺点。微软文档解释:

DBCC INDEXDEFRAG 命令是联机操作,所以索引只有在该命令正在运行时才可用。而且可以在不丢失已完成工作的情况下中断该操作。这种方法的缺点是在重新组织数据方面没有聚集索引的除去/重新创建操作有效。

重新创建聚集索引将对数据进行重新组织,其结果是使数据页填满。填满程度可以使用 FILLFACTOR 选项进行配置。这种方法的缺点是索引在除去/重新创建周期内为脱机状态,并且操作属原子级。如果中断索引创建,则不会重新创建该索引。


也就是说,要想获得好的效果,还是得用重建索引,所以决定重建索引。
DBCC DBREINDEX(表,索引名,填充因子)
第一个参数,可以是表名,也可以是表ID。
第二个参数,如果是'',表示影响该表的所有索引。
第三个参数,填充因子,即索引页的数据填充程度。如果是100,表示每一个索引页都全部填满,此时select效率最高,但以后要插入索引时,就得移动竺娴乃幸常屎艿汀H绻?,表示使用先前的填充因子值。

DBCC DBREINDEX(A,'',90)

五 本次案例分析:

近期调整一个SQL,耗时6秒,发现索引碎片非常多,重建索引

ddeclare @table_id int
set @table_id=object_id('TC005')
dbcc showcontig(@table_id)

DBCC SHOWCONTIG scanning 'TC005' table...
Table: 'TC005' (1453352342); index ID: 1, database ID: 8
TABLE level scan performed.
- Pages Scanned................................: 63766
- Extents Scanned..............................: 12031
- Extent Switches..............................: 63371
- Avg. Pages per Extent........................: 5.3
- Scan Density [Best Count:Actual Count].......: 12.58% [7971:63372] --该值接近100%为好
- Logical Scan Fragmentation ..................: 50.35% --该值接近0为好
- Extent Scan Fragmentation ...................: 92.39% --该值接近0为好
- Avg. Bytes Free per Page.....................: 6048.2
- Avg. Page Density (full).....................: 25.28%
DBCC execution completed. If DBCC printed error messages, contact your system administrator.


dbcc dbreindex('newjoyo2.dbo.TC005','',90)


再次检查:

declare @table_id int
set @table_id=object_id('TC005')
dbcc showcontig(@table_id)

DBCC SHOWCONTIG scanning 'TC005' table...
Table: 'TC005' (1453352342); index ID: 1, database ID: 8
TABLE level scan performed.
- Pages Scanned................................: 17678
- Extents Scanned..............................: 2232
- Extent Switches..............................: 2231
- Avg. Pages per Extent........................: 7.9
- Scan Density [Best Count:Actual Count].......: 99.01% [2210:2232] --已经接近100%
- Logical Scan Fragmentation ..................: 0.04%
- Extent Scan Fragmentation ...................: 0.40%
- Avg. Bytes Free per Page.....................: 707.6
- Avg. Page Density (full).....................: 91.26%
DBCC execution completed. If DBCC printed error messages, contact your system administrator.

再次TRACE跟踪了一下,发现CPU时间基本上在100毫秒以内:

示例数据如下:

TraceStart CPU READS WRITES DURATION CLIENT_PROCESSID SPID 2007-10-10 07:40:30.267
SQL:BatchCompleted Internet Information Services joyo2 62 21474 0 63 3784 286 2007-10-10 07:41:07.173
SQL:BatchCompleted Internet Information Services joyo2 62 21474 0 60 7496 233 2007-10-10 07:41:10.910
SQL:BatchCompleted Internet Information Services joyo2 46 478 0 46 4992 258 2007-10-10 07:41:19.847
SQL:BatchCompleted Internet Information Services joyo2 47 959 0 46 7496 233 2007-10-10 07:41:21.800
SQL:BatchCompleted Internet Information Services joyo2 0 128 0 0 3784 286 2007-10-10 07:41:42.503
SQL:BatchCompleted Internet Information Services joyo2 0 176 0 0 7496 233 2007-10-10 07:42:03.313
SQL:BatchCompleted Internet Information Services joyo2 0 525 0 0 3784 286 2007-10-10 07:42:20.597
SQL:BatchCompleted Internet Information Services joyo2 47 3264 0 60 4992 222 2007-10-10 07:42:21.723
SQL:BatchCompleted Internet Information Services joyo2 79 7871 0 76 3784 286 2007-10-10 07:42:47.783


效果仍然不理想,最后检查SQL,发现2个千万级大表关联没有用到CLUSTERED INDEX。该SQL已经无法再继续优化了,整个SQL大部分时间消耗在了BOOKMARK LOOKUP上。

结论:

对于BOOKMARK LOOKUP这个计划步骤产生的原因是由于没有使用CLUSTERED索引,而无法避免。

SQLSERVER索引有2大类,聚簇索引和非聚簇索引2种:聚簇索引和表是一体的(类似ORACLE的唯索引),而非聚簇索引节点保存了索引本身并且保存了聚簇索引的相关信息,查找时先找到对应的聚簇索引的值再去查找。

一个表只能有一个聚簇索引(数据的存储按照该所引得顺序物理存储,非常宝贵),所以建议设计时候要全面考虑以后的查询等(尤其是业务系统的大表),不建议用IDNETITY字段作聚簇索引。这个也是微软强烈建议的。

索引查找的效率应该是:

索引覆盖(当然这个有时候我们无法完全做到) > CLUSTERED INDEX SEEK > INDEX SEEK

查找了一下SQLSERVER文档解释:

Clustered Indexes

Clustered indexes have one row in sysindexes with indid = 1. The pages in the data chain and the rows in them are ordered on the value of the clustered index key. All inserts are made at the point the key value in the inserted row fits in the ordering sequence.

Microsoft® SQL Server™ 2000 indexes are organized as B-trees. Each page in an index holds a page header followed by index rows. Each index row contains a key value and a pointer to either a lower-level page or a data row. Each page in an index is called an index node. The top node of the B-tree is called the root node. The bottom layer of nodes in the index are called the leaf nodes. The pages in each level of the index are linked together in a doubly-linked list. In a clustered index, the data pages make up the leaf nodes. Any index levels between the root and the leaves are collectively known as intermediate levels.

For a clustered index, sysindexes.root points to the top of the clustered index. SQL Server navigates down the index to find the row corresponding to a clustered index key. To find a range of keys, SQL Server navigates through the index to find the starting key value in the range, and then scans through the data pages using the previous or next pointers. To find the first page in the chain of data pages, SQL Server follows the leftmost pointers from the root node of the index.

This illustration shows the structure of a clustered index.

最后再总结一下:

聚簇索引和表是一体的,表述据顺序是有序的(物理有序),所以聚簇索引对于查找非常有效,尤其是查询连续返回结果集比较大的情况,直接顺序返回大量值(如果用非聚簇索引可以想象有多么多的IO)。
一个表如果没有聚簇索引,表内数据是无序的,称之为堆存储(就是一大堆数据,无序)。
如果创建非聚簇索引(和表完全独立的结构),从索引行指向数据的称为行定位器,行定位器的结构取决于数据页的存储方式是堆集还是聚集。对于堆集,行定位器是指向行的指针;对于聚集,行定位器是聚集索引键值。
所以在聚集表中的非聚集索引会比在堆集表中的要慢。
频繁删除修改会造成聚集表很多碎片
text column会影响聚集表性能
SQLSERVE视图的一个索引默认识聚集索引,所以要慎重考虑这个索引,没有必要我们可以不去创建。
整理该文档时参考了以下地址和SQLSERVER联机文档:
posted on 2007-11-13 21:38  seeyou  阅读(1483)  评论(0编辑  收藏  举报