索引的碎片处理

本文介绍SQL Server中索引碎片的类型及如何通过REORGANIZE和REBUILD命令进行整理。区分内部碎片与外部碎片,并提供示例演示如何判断何时采用哪种方式优化索引。

SELECT OBJECT_NAME(dt.object_id),
si.name,
dt.avg_fragmentation_in_percent,
dt.avg_page_space_used_in_percent
FROM
(
SELECT object_id,
index_id,
avg_fragmentation_in_percent,
avg_page_space_used_in_percent
FROM    sys.dm_db_index_physical_stats (DB_ID(), NULL, NULL, NULL, 'DETAILED')
WHERE   index_id <> 0
)
AS dt --does not return information about heaps
INNER JOIN sys.indexes si
ON     si.object_id = dt.object_id
AND si.index_id  = dt.index_id


碎片的分类
1内部碎片(avg_page_space_used_in_percent)
-是说索引的大小,超过了索引的实际大小,也就是填充度
2外部碎片(avg_fragmentation_in_percent)
-是说索引页面的排序和物理不一致,index page的newxtpage+prevpageid的是最小相临的(REORGANIZE就是解决这个问题)
3物理连续性(fragment_count 和 avg_fragment_size_in_pages
[>65 and <256]-是说单一的分配单元的8个页面的连续性质,简单可以说是nextpage是不是当前page的+1至+7
4内部的逻辑碎片(有聚集索引的情况) 也就是槽号的物理顺序和逻辑顺序的不同(就是很多人说的聚集索引的顺序,和物理的存储顺序是不同的)

ALTER INDEX [index_id] ON [dbo].[tbTest] REORGANIZE
重新组织索引
REORGANIZE的过程:
    1压缩过程 查找临近的page,查找可以移动的记录转移到小pageid的页面,增加填充度(不会分配新的index page,有些记录会因为移动,而不在先前的page里存放)
    2使逻辑和物理的顺序保持一致(这和上面的不冲突),使用中间页来互换页面内容,使页面的pageid从小到大排列,删除最后的空page
ALTER INDEX [index_id] ON [dbo].[tbTest] REBUILD
重键索引
同时维护2个索引,新的创建完,会删除旧索引(因为同时维护2个版本的索引,只能分配新的索引页面,删除旧的索引页面)
所以rebuild后的avg_fragmentation_in_percent逻辑碎片,应该是在0
-3左右,avg_page_space_used_in_percent会在95以上(大型行也许会小一点)


--页拆分引起的碎片
if object_id('tbTest') is not null
drop table tbTest
go
create table tbTest(a int primary key,b varchar(1600))
go
insert into tbtest(a,b) select 5,replicate('a',1600)
insert into tbtest(a,b) select 10,replicate('b',1600)
insert into tbtest(a,b) select 15,replicate('c',1600)
insert into tbtest(a,b) select 20,replicate('d',1600)
insert into tbtest(a,b) select 25,replicate('e',1600)

dbcc ind([index],tbTest,1)

dbcc traceon(3604)

dbcc page([index],1,45,1)
             
insert into tbtest(a,b) select 22,replicate('f',1600)
用上面的查询看碎片的结果
(无列名)    name    avg_fragmentation_in_percent    avg_page_space_used_in_percent
tbTest    PK__tbTest__164452B1   
50    59.9085742525327

dbcc page([index],1,45,1)
--内部碎片avg_page_space_used_in_percent
select (8096-3245/* m_freeCnt*/)*1.0/8096=0.599184 基本可以对上
--逻辑碎片
avg_fragmentation_in_percent 这个的50,也许是[物理连续性]一类的碎片,因为外部的大小顺序倒是一样的,不深究了

dbcc page([index],1,80,1) 。。。

--判断REORGANIZE和REBUILD,重新组织和重建索引(注意,text,varchar(max)等要特殊处理,下次再说...:))

SELECT t.name as tablename,'ALTER INDEX [' + ix.name + '] ON [' + s.name + '].[' + t.name + '] ' +
      
CASE
             
WHEN ps.avg_fragmentation_in_percent > 15
             
THEN 'REBUILD'
             
ELSE 'REORGANIZE'
      
END +
      
CASE
             
WHEN pc.partition_count > 1
             
THEN ' PARTITION = ' + CAST(ps.partition_number AS nvarchar(MAX))
             
ELSE ''
      
END,
       avg_fragmentation_in_percent
FROM   sys.indexes AS ix
      
INNER JOIN sys.tables t
      
ON     t.object_id = ix.object_id
      
INNER JOIN sys.schemas s
      
ON     t.schema_id = s.schema_id
      
INNER JOIN
              (
SELECT object_id                   ,
                      index_id                    ,
                      avg_fragmentation_in_percent,
                      partition_number
             
FROM    sys.dm_db_index_physical_stats (DB_ID(), NULL, NULL, NULL, NULL)
              ) ps
      
ON     t.object_id = ps.object_id
         
AND ix.index_id = ps.index_id
      
INNER JOIN
              (
SELECT  object_id,
                       index_id ,
                      
COUNT(DISTINCT partition_number) AS partition_count
             
FROM     sys.partitions
             
GROUP BY object_id,
                       index_id
              ) pc
      
ON     t.object_id              = pc.object_id
         
AND ix.index_id              = pc.index_id
WHERE  ps.avg_fragmentation_in_percent > 10
  
AND ix.name IS NOT NULL
--上面语句是网上的,加了行数判断,也可以用页数量判断
and exists(
select * from sys.partitions where object_id=t.object_id and rows>10000
/*控制一下记录行数,因为小表的rebuild等会根据很多参数标准来判断是否要去重建。
网上搜索的判断因子:
生成查询计划的阀值
缓存机制,缓存的筛选,LRU算法
预读机制
checkpoint减少回滚距离
智能join判断
重编译
*/
)

<think>好的,我现在需要帮助用户理解索引碎片化的概念及其解决方法。首先,我得回忆一下索引碎片化的基本知识。索引碎片化通常发生在数据库表中,当数据频繁进行插入、更新或删除操作时,索引页会变得不连续,导致存储空间零散,影响查询性能。这时候,数据库需要更多的I/O操作来检索数据,从而拖慢查询速度。 接下来,我得考虑用户可能想知道的几个方面:原因、影响和解决方案。根据用户提供的引用内容,引用[2]提到了索引扫描是碎片整理的重点,而引用[3]则提到了使用VACUUM FULL来重组表和索引。另外,引用[4]中的分页查询例子可能和索引性能有关联,但用户的问题主要集中在碎片化上,所以需要重点整理相关引用信息。 首先,解释索引碎片化的原因。可能包括数据频繁修改导致页分裂,删除操作留下的空白空间未被重用,以及随机插入导致的数据分布不连续。这些原因需要结合数据库的工作原理来解释,确保用户理解为什么这些操作会导致碎片。 然后,说明碎片化的影响,比如查询效率下降、I/O增加、存储空间浪费等。这部分需要明确说明性能问题的根源,帮助用户认识到优化的重要性。 解决方案方面,引用中提到的VACUUM FULL是PostgreSQL的方法,而其他数据库可能有不同的工具,比如SQL Server的索引重组或重建。需要区分不同数据库的处理方式,但用户可能更关注通用方法,所以可以分别列出重组和重建的区别,以及自动维护策略,比如定期任务。引用[2]提到的索引向导和监控索引使用情况也很重要,需要纳入优化方案中。 另外,用户提供的引用[1]提到删除不必要的索引,这也是优化的一部分,可能和碎片整理相关,因为过多的索引会增加维护负担,导致更多碎片。需要指出在优化时,应评估并移除冗余索引。 最后,生成相关问题,确保覆盖用户可能进一步想了解的内容,比如检测碎片化的方法、不同数据库的处理差异、自动维护策略,以及碎片化与索引类型的关系等。 需要检查是否所有信息准确,特别是引用部分是否正确对应。例如,引用[3]确实提到了VACUUM FULL,而引用[2]讨论了索引扫描和碎片整理的重要性。确保回答结构清晰,分点明确,并且语言符合中文习惯,避免技术术语使用不当。</think>### 索引碎片化的概念与解决方法 #### **索引碎片化的原因** 1. **频繁的数据修改** - **插入/更新操作**:当数据页被填满时,新插入或更新的数据可能导致页分裂,形成不连续的存储空间。例如,随机插入非连续键值会导致索引逻辑顺序与物理存储顺序不一致[^2]。 - **删除操作**:删除数据后,索引页中可能留下空白空间,若未被新数据及时填充,会产生“空洞”。 2. **数据分布不均匀** 如果索引键值的分布范围变化较大(如自增主键突然跳跃),可能导致索引页利用率降低。 3. **存储引擎特性** 某些存储引擎(如InnoDB的B+树结构)在动态更新时更容易产生碎片。 --- #### **索引碎片化的影响** - **查询性能下降**:碎片化会增加磁盘I/O次数,例如需要扫描更多物理页才能获取逻辑连续的数据。 - **空间浪费**:碎片导致索引占用更多存储空间。 - **索引维护成本升高**:数据库优化器可能因碎片化选择低效的执行计划。 --- #### **优化方案** 1. **索引重建(Rebuild)** - **作用**:完全重建索引,消除碎片并重新组织数据页。 - **操作示例**: ```sql -- SQL Server ALTER INDEX index_name ON table_name REBUILD; -- PostgreSQL REINDEX INDEX index_name; -- MySQL (InnoDB) OPTIMIZE TABLE table_name; ``` - **适用场景**:碎片化严重(如碎片率 > 30%)时使用。 2. **索引重组(Reorganize)** - **作用**:仅整理现有页的物理顺序,不重新分配存储空间,开销较小。 - **操作示例**: ```sql -- SQL Server ALTER INDEX index_name ON table_name REORGANIZE; ``` - **适用场景**:轻度碎片化(碎片率 10%~30%)[^3]。 3. **定期维护策略** - **自动化任务**:通过脚本或工具(如SQL Server维护计划)定期检查并处理碎片。 - **监控工具**:利用数据库内置视图(如`sys.dm_db_index_physical_stats`)监控碎片率[^2]。 4. **减少碎片产生** - **避免频繁小事务**:批量处理更新/删除操作。 - **合理设计索引**:减少冗余索引,优先使用覆盖索引[^1][^4]。 - **填充因子(Fill Factor)**:设置合适的填充因子(如90%),预留空间以减少页分裂。 5. **使用VACUUM(PostgreSQL)** - **作用**:回收碎片空间并压缩数据。 - **操作示例**: ```sql VACUUM FULL table_name; -- 完全重组表及索引[^3] ``` --- #### **碎片化检测方法** - **SQL Server**: ```sql SELECT index_id, avg_fragmentation_in_percent FROM sys.dm_db_index_physical_stats(DB_ID(), NULL, NULL, NULL, 'LIMITED'); ``` - **MySQL(InnoDB)**: 通过`SHOW TABLE STATUS LIKE 'table_name'`查看`Data_free`字段,表示未利用的碎片空间。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值