zoukankan      html  css  js  c++  java
  • 索引调优 第一篇:重建、填充和查找

    在做性能优化时,经常需要创建索引,维护索引,或重建,或重组;在创建索引时,索引的数据页有时需要填充满,有时需要预留一定比例的空闲空间;在分析查询的执行计划之后,推荐创建覆盖索引(covering index),优化查询语句,使用执行计划通过Index Seek来获取少量数据等,这些都是索引优化不得不知的要点。

    一,索引的重组(Reorganize)和重建(Rebuild)

    在SQL Server中,索引(Index)是B-Tree(balance tree)结构,每个Page之间都有双向指针链接在一起。Index是在table结构之外,独立存在的存储结构。Index能使查询性能带来飞跃的主要原因是:Index 结构更小,能够更快加载到内存;Index ey物理顺序和逻辑一致,数据的预读取能够提高数据的加载速度,SQL Server 每次读取操作都会将物理物理相邻的多个Page一起加载到内存。

    BTree结构决定 Index 的叶子节点,从左到右使依次增大,如图是Index的叶子节点,左边的Index Key最小,右边的Index Key最大:

    如果更新数据导致index key变化,例如,将index key 由4变更为9,那么必须将9放置在8之后,10之前,如果8所在的Page有空间容纳9,那么SQL Server只需要将9移动到8之后,原来的4被删除,这会降低原page中数据存储的密度,造成一个碎片(fragment),即:3和5之间存在空闲空间,但是物理顺序和逻辑顺序还是一致的。

    如果8和10所在的page不能容纳9,那么 SQL Server 选择最节省,最有效的方式:拆分Page。试想,如果不拆分page,那么,5,6,7,8 这几个数据行都要向前移动,为9腾挪空间。在SQL Server中,数据移动是十分浪费IO,内存和CPU资源的,IO必须在CPU的调控下进行。

    拆分Page是指分配一个新的Page,将8所在的Page上的数据的一半(后一半,或前一半)移动在新的Page上,如图,将page中的后一半移动在新的page上,通过指针连接在一起,保持逻辑顺序的一致性,但是物理顺序已经不连续了。

    对于Index Key移动之后,其物理顺序和逻辑顺序仍然保持一致,这会导致索引出现碎片,数据存储的密度降低;而拆分page,不仅将page存储数据的密度降低一半,而且数据的物理顺序和逻辑顺序,导致SQL Server的预读取操作效果下降。针对Index的这两种情况,根据Index的碎片率,对Index 进行重组(Reorganize)或重建(Rebuild)。

    1,索引Reorganize 和 Rebuild的过程 

    Rebuild 是重新创建,将索引占用的原有存储空间释放,重新申请空间来创建索引结构,这意味着,SQL Server存储引起需要为索引结构分配新的数据页,在重建索引结构时,索引占用的存储空间是其实际大小的2倍还多。

    Reorganize 是重新组织索引结构的叶子节点。在重组索引时,SQL Server存储引擎首先按照原有的填充因子(Fillfactor),压缩索引结构的叶子节点;然后,使用相同的数据页,把索引结构的叶子节点重新组织,使叶子节点符合索引定义的逻辑顺序。重组索引不会分配新的数据页,只占用索引最初占用的存储空间。

    ALTER INDEX { index_name | ALL }
    ON schema.table
    REBUILD | REORGANIZE 

    2, 重建索引

    在重建索引时,SQL Server 存储引擎使用索引的定义元数据,就是说,按照索引键(index key),索引类型(index type),唯一属性和排序方向重新创建索引。

    • 重建索引,将使被disable的索引重新启用;
    • 重建聚集索引时,不会重建与之关联的非聚集索引(nonclustered index),除非指定all关键字;all关键字指定基础表中的所有索引。
    • 如果指定all关键字,而基础表(underlying table)是堆,那么重建索引的操作对基础表没有任何影响;而与基础表相关的所有非聚集索引都将会重建;

    在重新创建索引(Rebuild)时,如果没有指定索引选项(Index Option),Rebuild操作使用默认的索引选项。在SQL Server 2012版本中,共有11个索引选项,其中5个索引选项的元数据存储在sys.indexes 中,分别是 ignore_dup_key、fill_factor、is_padded、allow_row_locks、allow_page_locks,其他6个索引选项使用默认值,其默认值(Default value)都是“否定的”或0,如下列表所示:

    SORT_IN_TEMPDB :            Default OFF
    STATISTICS_NORECOMPUTE :    Default OFF
    DROP_EXISTING:              Default OFF
    ONLINE:                     Default OFF
    DATA_COMPRESSION :          Default NONE
    MAXDOP:                     0

    查看 sys.indexes 存储的索引选项:

    select i.object_id,i.name as IndexName,i.index_id,i.type,i.type_desc,
        i.data_space_id,i.is_disabled,
    
        --Unique Property
        i.is_unique,
        
        --Constraint
        i.is_primary_key,
        i.is_unique_constraint,
    
        --Filter Index
        i.has_filter,
        i.filter_definition,
    
        --Index Options
        i.ignore_dup_key,
        i.fill_factor,
        i.is_padded,
        i.allow_row_locks,
        i.allow_page_locks
    from sys.indexes i
    View Code

    3,重组索引

    重组索引结构的叶子节点,由于SQL Server默认以联机方式重组索引,这意味着在索引重组事务执行的过程中,SQL Server 存储引擎不会长期阻塞表锁,能够对基础表(underlying table)执行更新操作。如果索引选项ALLOW_PAGE_LOCKS设置为OFF,或索引被禁用,那么不能重组索引。

    二,索引的填充属性(FillFactor 和 PAD_INDEX)

    在Create Index时,必须考虑属性FillFactor 和 PAD_INDEX的设置,这两个属性只在create index 或 rebuild index时起作用,表示索引页的填充程度,在索引结构中,按照结点在BTree结构中的位置,索引页分为:叶级结点,中间结点。一个数据页(Page)的大小是8KB。

    • FillFactor 属性指定索引叶级结点的填充百分比,微软建议设置FillFactor=90;
    • Pad_index 属性是boolean 类型,指定是否使用 FillFactor 来填充索引的中间节点;默认值是OFF;
    • 在插入数据时,SQL Server 尽可能使用Page全部的空间,而不会考虑填充属性;填充属性只影响索引的创建;
    PAD_INDEX = { ON | OFF }
    FILLFACTOR = fillfactor 

    在创建索引时,设置FillFactor=90,这意味着,SQL Server 在create index 或 rebuild index时,不是将索引页的全部空间用完,而是使用Page空间的90%,预留10%的空闲空间,预留的Page内部的空间叫做内部碎片(Internal Fragmentation)。预留一定比例的Fragmentation的作用是:减少Page拆分。预留比例需要折衷查询和更新操作:预留空间太多,能够减少Page Split,提高数据更新速度,但预留空间过多导致索引碎片(Index Fragmentation)太多,降低查询性能;预留空间太少,能够最大限度减少Index fragmentation,提高查询查询性能,但是一旦数据更新,会导致Page Split,产生外部碎片,降低查询性能。建议预留10% 的空间,这样既能提高数据更新的速度,也能兼顾数据的更新。

    1,索引中间节点的填充属性PAD_INDEX

    PAD_INDEX属性表示中间节点填满的程度,100减去该属性值,就是索引页预留的空闲空间的百分比,默认值是OFF;如果设置为ON,那么在索引 create 或 rebuild时,SQL Server 将使用FillFactor百分比来填充中间节点,因此,需要指定FILLFACTOR。

    2,索引叶子节点的填充属性 FILLFACTOR

    FillFactor选项是一个整数值,有效值是从1到100,该属性表示叶子节点填满的程度,该属性的默认值是0,和FillFactor=100行为相同,表示在创建索引或重建索引时,数据页不会预留空闲空间。

    3,推荐在创建索引时,显式指定填充属性

    填充属性只在创建索引(create),或重建索引(rebuild)时起作用。如果填充因子在1和100之间,那么创建聚集索引将分配更多的数据页,带来的好处是减少了页拆分操作的次数。

    例如,在创建索引(create),或重建索引(rebuild)时,指定FillFactor=80,表示每个叶子节点留下20%的空闲空间,当新的数据插入到基础表(underlying table)时,SQL Server使用该20%的空闲空间来容纳新的数据。

    在创建index 或重建 index时,指定 fillfactor 和 pad_index 属性:

    --rebuild index
    ALTER INDEX index_name
    ON schema.table
    REBUILD 
    WITH (fillfactor = 90,pad_index=on,data_compression=page);
    
    --create index
    CREATE INDEX index_name
    ON schema.table
    (indexkey) 
    WITH (DROP_EXISTING = ON,fillfactor = 90,pad_index=on,data_compression=page);
    View Code

    三,索引的查找(Index Seek)和扫描(Index Scan)

    索引是B树结构,在执行查询时,Index Seek是指SQL Server从索引结构的根节点(Root Node),逐级向叶节点(Leaf Node)查找;在查找到相应叶子节点后,取出叶子节点的数据。对于聚集索引,叶子节点是整个表的数据,Index Seek能够获取到所有列的数据,而对于非聚集索引,叶子节点存储的是索引列的数据,如果索引有包含列,那么叶子节点中也存储包含列的数据,Index Seek只能获取索引列和包含列的数据;如果查询还需要返回其他列的数据,那么SQL Server必须根据索引叶子节点包含的“行地址”信息到基础表(或聚集索引)中去获取数据,这就是进行书签查找(key lookup)。Index Seek用于从大数据量的表中返回少量记录的查询。

    Index Scan是直接遍历索引树的所有叶子节点,对于包含聚集索引的基础表,只能进行Index Seek或Index Scan,因为,聚集索引的叶子包含所有的数据。对于堆表,当需要返回所有列的数据,SQL Server有时会选择执行Table Scan。Table Scan是对全表进行逐行的扫描,即使数据表中只有一行数据匹配,也会将所有数据匹配一遍,微软建议,始终在数据表上创建聚集索引。

    参考文档:

    Reorganize and Rebuild Indexes

    Index Seek和Index Scan的区别以及适用情况

    SQL SERVER – Index Seek vs. Index Scan – Diffefence and Usage – A Simple Note

  • 相关阅读:
    Something I know about WebDynpro
    Details about support package implementation
    CRM Middleware Performance Topics
    Way to configure the logon navigaion layouts via Business Roles in CRM
    DOM 常用节点类型和方法
    第一届 xdef 会议日程
    去除百度音乐盒广告的chrome插件 持续更新
    从人人网抓取高校数据信息,包括,省份 高校 院系 (提供最终SQL文件下载)
    PHP 与 JSON
    解决HTTPS 发送请求走socket问题
  • 原文地址:https://www.cnblogs.com/ljhdo/p/5136024.html
Copyright © 2011-2022 走看看