【SQL Server】系统学习之二:索引优化

页大小8192个字节,行限制为8060字节(大型对象除外)。

包含varchar nvarchar varbinary sql_variant(8012,object类型) clr 的行,如果行大小超过8060,最大的上述列,被移动到行溢出分配单元。如果列超过8000,还会被存储为大型对象。

分配单元:IN_ROW_DATA

              Row_OVERFLOW_DATA

              LOB_DATA

分配顺序扫描:基于IAM页(索引分配映射)

一个IAM只能映射某一个表分区的某一种类型的页面,并且最多覆盖4GB的大小。比如:一个表有四个分区,每个分区都包含三种类型的数据(in-row, LOB, and row-overflow),那它最少会有4*3 = 12 个IAM页面,若个任何一个分区的任何一种数据类型超过4G或任意一个表分区跨越多个文件,就需要更多的IAM。

索引顺序扫描

索引碎片:分配新页,把大约一半的行的移动到新页,在根据键值将新行插入到新页或旧页,并调整链表,以反映正确的逻辑顺序。例外是新行键值是最大的,将直接分配新页,避免过多的页拆分。

索引的级数:indexproperty

索引查找是随机IO,为每行lookup一次,都是读取一个页(堆)或 N个页(聚集表,索引N级)

非聚集索引包括:数据副本,行定位符【堆的RID(文件号-页号-行号),聚集表中的KeyID(聚集索引键值和唯一标示符)】

===============================影响查询速度的重点是磁盘臂的摆动

select indexproperty(object_id('new_FollowRecords'),'PK_new_FollowRecords','IndexDepth')

查询方法:

1、表扫描

对无聚集索引的堆表,分配顺序扫描,逻辑读取次数等于该表使用的页数

2、无序聚集索引扫描

表包含聚集索引,且查询时未指定索引排序操作

3、无序覆盖非聚集索引扫描

插播2、3会根据数据一致性和性能选择分配顺序扫描和索引顺序扫描

a、索引非常小(小于64页)时,解释IAM页的开销成为主要开销,会使用索引顺序扫描

b、小表(?不知道多大算小),优先选择分配顺序扫描

c、索引大小超过64页;未提交读隔离级别;使用分配顺序扫描(不要求数据一致性)

d、索引大小超过64页,更高的隔离级别,但是知道不允许对数据进行修改(例如使用tablock,例如只读的文件组、数据库)

4、有序聚集索引扫描

索引顺序扫描

5、有序覆盖非聚集索引扫描

 6、非聚集索引查找+有序局部扫描+lookups

适用于小范围查询或点查询(选择性高),筛选的列是索引的第一个键列。

通过查询计划里的Seek predicates 和Order:True推测,执行了有序局部扫描

7、无序非聚集索引扫描+lookups  

当筛选的列不是索引的第一个键列,没办法,只能扫描。

要求同样的,选择性足够高用这种方式,否则过多的lookup必然带来过高的开销。

8、聚集索引查找+有序局部扫描

平凡的计划:不依赖于选择性,不会因为查询的数据量变大,就性能降低。

9、覆盖非聚集索引查找+有序局部扫描

8和9都受索引碎片影响,因为查询开销主要在局部扫描上

索引的覆盖列,仅仅存在于叶级页,当索引顺序改变时,并不会像键列一样跟着被更新。

索引交集:可以看做是按行定位符进行的内连接,从一定程度上缓解创建大量索引所带来的维护开销。

筛选索引:在创建索引时,指定where子句,限定范围,例如xx is not null, a<4000.这样,只会在有限范围内创建索引,开销更小些。

查询索引使用情况:

select * from dm_db_index_usage_stats:查询用户、系统的查找、扫描次数,以及最后一次执行时间,用于分析是否该索引还被使用。

select * from dm_db_index_operational_stats(DB_ID('数据库名'),object_id('表名'),index_id('索引名'),partition_number(分区id)

默认值是null,indexid如果是堆,指定0;

碎片整理:

重新生成:按链表顺序重新生成索引,使页的文件顺序和链表顺序一致。

重新组织:冒泡排序算法,根据索引页在链表中的顺序在磁盘上物理的将页排序。

原文地址:https://www.cnblogs.com/xyang/p/3889099.html