zoukankan      html  css  js  c++  java
  • 索引碎片整理

    数据库表A有十万条记录,查询速度本来还可以,但导入一千条数据后,问题出现了。当选择的数据在原十万条记录之间时,速度还是挺快的;但当选择的数据在这一千条数据之间时,速度变得奇慢。 
      凭经验,这是索引碎片问题。检查索引碎片DBCC SHOWCONTIG(表),得到如下结果: 
      DBCC SHOWCONTIG 正在扫描 'A' 表……   表: 'A'(884198200);索引 ID: 1,数据库 ID: 13   已执行 TABLE 级别的扫描。   - 扫描页数……: 3127   - 扫描扩展盘区数……: 403   - 扩展盘区开关数……: 1615 
      - 每个扩展盘区上的平均页数……: 7.8 
      - 扫描密度[最佳值:实际值]……: 24.20%[391:1616]   - 逻辑扫描碎片……: 68.02% 
      - 扩展盘区扫描碎片……: 38.46% 
      - 每页上的平均可用字节数……: 2073.2 
      - 平均页密度(完整)……: 74.39% 
      DBCC 执行完毕。如果 DBCC 输出了错误信息,请与系统管理员联系。   由上我们看出,逻辑扫描碎片和扩展盘区扫描碎片都非常大,果真需要对索引碎片进行处理了。 
      一般有两种方法解决,一是利用DBCC INDEXDEFRAG整理索引碎片,二是利用DBCC DBREINDEX重建索引。二者各有优缺点。调用微软的原话如下: 
      DBCC INDEXDEFRAG 命令是联机操作,所以索引只有在该命令正在运行时才可用。而且可以在不丢失已完成工作的情况下中断该操作。这种方法的缺点是在重新组织数据方面没有聚集索引的除去/重新创建操作有效。 
      重新创建聚集索引将对数据进行重新组织,其结果是使数据页填满。填满程度可以使用 FILLFACTOR 选项进行配置。这种方法的缺点是索引在除去/重新创建周期内为脱机状态,并且操作属原子级。如果中断索引创建,则不会重新创建该索引。 
      也就是说,要想获得好的效果,还是得用重建索引,所以决定重建索引。   DBCC DBREINDEX(表,索引名,填充因子)   第一个参数,可以是表名,也可以是表ID. 
      第二个参数,如果是'',表示影响该表的所有索引。 
      第三个参数,填充因子,即索引页的数据填充程度。如果是100,表示每一个索引页都全部填满,此时select效率最高,但以后要插入索引时,就得移动后面的所有页,效率很低。如果是0,表示使用先前的填充因子值。   DBCC DBREINDEX(A,'',100)

    描述:

    一碎片种类

    1 内部碎片,又称为平均页密度。是指索引正在占有超过它实际所需的空间大小。

    它具有两面型:低百分比会对读取数据的查询产生负面影响,会涉及更多读取操作,因为如果页被填充满的话,

    只需读取更少的页;另一方面,如果如果在创建索引时设置一个较低的填充因子,就可以避免当插入更多记录而不

    必进行页拆分。

    对应sys.dm_db_index_physical_stats的列avg_page_space_used_in_percent。

     

    2 外部碎片,又称平均碎片百分比,或逻辑碎片。是指在分页的逻辑顺序与物理顺序不匹配或者索引拥有的扩展不

    连续时产生。包括以下两种:

    逻辑碎片:这是索引的叶级页中出错页所占的百分比。出错页是指在IAM 中所指示的下一页不同于由叶级页中的下一页指针所指向的页。

    区碎片(有的书翻译成:扩展碎片):这是堆的叶级页中出错区所占的百分比。出错区是指:包含堆的当前页的区不是物理上的包含前一页的区后的下一个区。

    这种碎片对索引的有序扫描操作具有非常显著的影响。它会对那些不依赖于索引链接的列表的操作(例如:查找操作,lookup操作,无序扫描)不产生影响。

    对应sys.dm_db_index_physical_stats的列avg_fragmentation_in_percent 。因此为了获得最佳性能,avg_fragmentation_in_percent 的值应尽可能接近零。

    但是,从0 到10%范围内的值都可以接受。所有减少碎片的方法(例如重新生成、重新组织或重新创建)都可用于降低这些值。

     

    二碎片的检测

    使用系统函数sys.dm_db_index_physical_stats 可以检测特定索引、表或索引视图的所有索引、一个数据库中的所有索引或所有数据库中的所有索引中的碎片。

     

    扫描模式如下:

    LIMITED 模式运行最快,扫描的页数最少。对于堆,它将扫描所有页,但对于索引,则只扫描叶级上面的父级别页。

    在该模式下,以下列将显示为NULL:avg_page_space_used_in_percent,record_count,min_record_size_in_bytes,max_record_size_in_bytes,avg_record_size_in_bytes

    SAMPLED 模式将返回基于索引或堆中所有页的1%样本的统计信息。如果索引或堆少于10,000 页,则使用DETAILED 模式代替SAMPLED。

    DETAILED 模式将扫描所有页并返回所有统计信息。

     

    需要重点关注的几列:

    index_id 索引的索引ID。= 堆。

    avg_fragmentation_in_percent 索引的逻辑碎片或堆的区碎片。

    注:因此为了获得最佳性能,avg_fragmentation_in_percent 的值应尽可能接近零。

    avg_page_space_used_in_percent 内部碎片

    fragment_count 索引中的碎片(物理上连续的叶页)数量

    avg_fragment_size_in_pages 索引中一个碎片的平均页数。

    注:

    该值大于个分页,扫描性能会相当不错,上限为个分页,如果超过个分页,性能提升不明显。

    碎片由分配单元中同一文件内的物理连续的叶级页组成。一个索引至少有一个碎片。索引可以包含的最大碎片数等于索引的页级别页数。碎片越大,意味着读取相同页数所需的磁盘I/越少。因此,avg_fragment_size_in_pages 值越大,范围扫描的性能越好。avg_fragment_size_in_pages 和avg_fragmentation_in_percent 值成反比。因此,重新生成或重新组织索引会减少碎片数量,但同时增大碎片大小。

    forwarded_record_count 堆中的前推记录数。

    注:如果数据表拥有的许多前推记录数,全表扫描就会非常有效率。

    ghost_record_count 分配单元中将被虚影清除任务删除的虚影记录数。

    version_ghost_record_count 由分配单元中未完成的快照隔离事务保留的虚影记录数。

    注:虚影记录数是物理上仍然存在于分页上,但逻辑上已经被删除的记录。

    如果存在大量的虚影记录,数据表就具有大量的内部碎片的缺陷且没有任何益处。这些虚影记录

    在所有相关事务被提交或者回滚以前都不会被清除。

     

    --查询脚本

    --为防止db_id和object_id无效时,会返回NULL,而不会报错,这样就会查询所有数据库和数据库中的全部对象,

    --而不是我们要查询的数据库和数据库对象

    DECLARE @db_id SMALLINT;

    DECLARE @object_id INT;

     

    SET @db_id = DB_ID(N'AdventureWorks');

    SET @object_id = OBJECT_ID(N'AdventureWorks.Person.Address');

     

    IF @db_id IS NULL

    BEGIN;

        PRINT N'Invalid database';

    END;

    ELSE IF @object_id IS NULL

    BEGIN

        PRINT N'Invalid object';

    END;

     

    SELECT * FROM sys.dm_db_index_physical_stats

              (@db_id, @object_id, NULL, NULL, NULL);

             

     

    三碎片移除

    重新组织

    范围:avg_fragmentation_in_percent 的值在到之间     

     

    ALTER INDEX idx_cl_od ON dbo.Orders REORGANIZE;

     

    重新生成

    范围:avg_fragmentation_in_percent 值大于或avg_fragment_size_in_pages值小于

     

    ALTER INDEX idx_cl_od ON dbo.Orders REBUILD WITH (ONLINE = ON);

     

    四索引重建

     

    ALTER INDEX PK_TransactionHistory_TransactionID

               ON Production.TransactionHistory REBUILD;

    -- 推荐DROP_EXISTING属性

    CREATE UNIQUE CLUSTERED INDEX PK_TransactionHistory_TransactionID

                  ON Production.TransactionHistory

                       (TransactionDate, TransactionID)

                  WITH DROP_EXISTING;

     

    --将自动重新组织或重新生成数据库中平均碎片超过10%的所有分区

     -- ensure a USE <databasename> statement has been executed first.

    SET NOCOUNT ON;

    DECLARE @objectid int;

    DECLARE @indexid int;

    DECLARE @partitioncount bigint;

    DECLARE @schemaname sysname;

    DECLARE @objectname sysname;

    DECLARE @indexname sysname;

    DECLARE @partitionnum bigint;

    DECLARE @partitions bigint;

    DECLARE @frag float;

    DECLARE @command varchar(8000);

    -- ensure the temporary table does not exist

    IF EXISTS (SELECT name FROM sys.objects WHERE name = 'work_to_do')

        DROP TABLE work_to_do;

    -- conditionally select from the function, converting object and index IDs to names.

    SELECT

        object_id AS objectid,

        index_id AS indexid,

        partition_number AS partitionnum,

        avg_fragmentation_in_percent AS frag

    INTO work_to_do

    FROM sys.dm_db_index_physical_stats (DB_ID(), NULL, NULL , NULL, 'LIMITED')

    WHERE avg_fragmentation_in_percent > 10.0 AND index_id > 0;

    -- Declare the cursor for the list of partitions to be processed.

    DECLARE partitions CURSOR FOR SELECT * FROM work_to_do;

     

    -- Open the cursor.

    OPEN partitions;

     

    -- Loop through the partitions.

    FETCH NEXT

       FROM partitions

       INTO @objectid, @indexid, @partitionnum, @frag;

     

    WHILE @@FETCH_STATUS = 0

        BEGIN;

            SELECT @objectname = o.name, @schemaname = s.name

            FROM sys.objects AS o

            JOIN sys.schemas as s ON s.schema_id = o.schema_id

            WHERE o.object_id = @objectid;

     

            SELECT @indexname = name

            FROM sys.indexes

            WHERE  object_id = @objectid AND index_id = @indexid;

     

            SELECT @partitioncount = count (*)

            FROM sys.partitions

            WHERE object_id = @objectid AND index_id = @indexid;

     

    -- 30 is an arbitrary decision point at which to switch between reorganizing and rebuilding

    IF @frag < 30.0

        BEGIN;

        SELECT @command = 'ALTER INDEX ' + @indexname + ' ON ' + @schemaname + '.' + @objectname + ' REORGANIZE';

        IF @partitioncount > 1

            SELECT @command = @command + ' PARTITION=' + CONVERT (CHAR, @partitionnum);

        EXEC (@command);

        END;

     

    IF @frag >= 30.0

        BEGIN;

        SELECT @command = 'ALTER INDEX ' + @indexname +' ON ' + @schemaname + '.' + @objectname + ' REBUILD';

        IF @partitioncount > 1

            SELECT @command = @command + ' PARTITION=' + CONVERT (CHAR, @partitionnum);

        EXEC (@command);

        END;

    PRINT 'Executed ' + @command;

     

    FETCH NEXT FROM partitions INTO @objectid, @indexid, @partitionnum, @frag;

    END;

    -- Close and deallocate the cursor.

    CLOSE partitions;

    DEALLOCATE partitions;

     

    -- drop the temporary table

    IF EXISTS (SELECT name FROM sys.objects WHERE name = 'work_to_do')

        DROP TABLE work_to_do;

    GO

     

    五存在疑问

    填充因子的大小,该如何决定呢?

    “碎片越大,意味着读取相同页数所需的磁盘I/越少”,这句话不太理解。

    虚影记录,该如何处理?     

     

     解答:

     它依赖于应用程序对SQLServer表的读和写的比率。首要的原则,按照下面的指导:

          低更改的表(读写比率为:):%的填充因子

          高更改的表(写超过读):-70%的填充因子

          读写各一半的:-90%的填充因子

  • 相关阅读:
    Ubuntu 14.04 设置静态IP
    Spring MVC 入门就这一篇
    一站式轻量级框架 Spring
    深入 Web 请求过程
    使用 Docker Compose 搭建 Nexus 依赖私服及使用配置
    Spring Cloud Alibaba 与 Dubbo 的完美融合
    使用 Spring Cloud Alibaba Nacos Config 作为配置中心
    使用 Spring Cloud Alibaba Nacos 实现服务注册与发现
    Spring Boot 监听 Redis Key 失效事件实现定时任务
    使用 Spring Boot Admin 监控服务
  • 原文地址:https://www.cnblogs.com/houzuofeng/p/3324575.html
Copyright © 2011-2022 走看看