SQL Server查询性能优化――堆表、碎片与索引(一)(一)

2014-11-24 09:53:10 · 作者: · 浏览: 0

SQL Server在堆表中查询数据时,是不知道到底有多少数据行符合你所指定的查找条件,它将根据指定的查询条件把数据表的全部数据都查找一遍。如果有可采用的索引,SQL Server只需要在索引层级查找每个索引分页的数据,再抓出所需要的少量数据分页即可。访问数据表内数以万计的数据分页与只访问少数索引的分页两者间的差异,让索引变成效能调校的最佳工具。
堆表的结果示意图:
\
堆表内的数据页和行没有任何特定的顺序,也不链接在一起。数据页之间唯一的逻辑连接是记录在 IAM 页内的信息。
假设订单明细表中有100万条数据,需要查询某个订单的明细数据,如下:
select* from T_EPZ_INOUT_ENTRY_DETAIL where entry_apply_id='31227000034000090169'
如果在堆表中进行查询,SQL Server通过扫描 IAM 页对堆表进行全表扫描,对entry_apply_id比较100万次,如果以entry_apply_id字段建立索引,则因为索引键值数据都必定以B-Tree有顺序的摆放,所以可采用二分查找找数据。也就是2的N次方大于记录数,就可以找到该条数据。而2的20次方大于100万,因此最多找寻20次就可以找到该条记录。20次与100万次的比较,你可以轻松感受出性能的差异。
下面我们举个实例来做说明:
一、表空间的高度碎片化
1.此表的碎分布信息,从下图中可以看出此表的有非常多的内部碎片与外部碎片。
a) 此表的平均页密度只有24%,也就是说平均一页只有1/4空间才有数据,其他的3/4空间都是空着,有着很多的内部碎片。
b) 此表的扫描密度只有13%,也就是说理论上的区的数量与实现上区的数量之比为1:7.5,也就是说存在非常多的外部碎片,也就是说每个区的利用率相当低,一个区的数据全部加起来,才一个数据页。
如下图。
\

对字段的说明:(例二、例三中的图中字段说明是一样的。)
Pages:如果在DBCC SHOWCONTIG 语句中指定了index_id,则将遍历指定索引的叶级上的页链,索引为叶子层使用的分页数目。如果只指定 table_id,或者 index_id 为 0,则将扫描指定表的数据页。
AvgeragePageDensity:平均页密度(为百分比)。该值考虑行大小,所以它是页的填满程度的更准确表示。百分比越大越好。
ScanDensity:扫描密度(为百分比)。这是“BestCount”与“ActualCount”的比率。如果所有内容都是连续的,则该值为 100;如果该值小于 100,则存在一些碎片。
2. SQL查询语句与查询执行计划成本
--查询语句:
SET STATISTICS IO on
go
SET STATISTICS TIME on
go
select * from T_EPZ_INOUT_ENTRY_DETAIL where entry_apply_id='31227000034000090169'
go
SET STATISTICS IO off
go
SET STATISTICS TIME off
go
\
3.查询所需要的时间与I/O
SQL Server 执行时间:
CPU 时间 = 0 毫秒,耗费时间 = 0 毫秒。
SQL Server 分析和编译时间:
CPU 时间 = 16 毫秒,耗费时间 = 76 毫秒。
SQL Server 分析和编译时间:
CPU 时间 = 0 毫秒,耗费时间 = 0 毫秒。
(所影响的行数为 13 行)
表 'T_EPZ_INOUT_ENTRY_DETAIL'。扫描计数 1,逻辑读 4825 次,物理读 6 次,预读 19672 次。
SQL Server 执行时间:
CPU 时间 = 47 毫秒,耗费时间 = 10544 毫秒。
SQL Server 分析和编译时间:
CPU 时间 = 0 毫秒,耗费时间 = 0 毫秒。
SQL Server 执行时间:
CPU 时间 = 0 毫秒,耗费时间 = 0 毫秒。
SQL Server 分析和编译时间:
CPU 时间 = 0 毫秒,耗费时间 = 0 毫秒。
二、表低度碎片化
1. 此表的碎分布信息,从下图中可以看出此表的有非常多的内部碎片与外部碎片。
a) 此表的平均页密度只有97%,也就是说数据差不多把一个数据页都塞满了,没有多余的空间,没有内部碎片。
b) 此表的扫描密度只有98%,也就是说理论上的区的数量与实现上区的数量之比为1:1,也就是说基本上没有外部碎片,也就是说每个区的利用率相当高。
如下图。
\
备注:对于上图中的一些字段说明,见(一)。
2.SQL查询语句与查询执行计划成本
--查询语句:
SET STATISTICS IO on
go
SET STATISTICS TIME on
go
select * from T_EPZ_INOUT_ENTRY_DETAIL where entry_apply_id='31227000034000090169'
go
SET STATISTICS IO off
go
SET STATISTICS TIME off
go
\

3.查询所需要的时间与I/O
SQL Server 执行时间:
CPU 时间 = 0 毫秒,耗费时间 = 0 毫秒。
SQL Server 分析和编译时间:
CPU 时间 = 0 毫秒,耗费时间 = 92 毫秒。
SQL Server 分析和编译时间:
CPU 时间 = 0 毫秒,耗费时间 = 0 毫秒。
(所影响的行数为 13 行)
表 'T_EPZ_INOUT_ENTRY_DETAIL'。扫描计数 1,逻辑读 1205 次,物理读 0 次,预读 1209 次。
SQL Server 执行时间:
CPU 时间 = 16 毫秒,耗费时间 = 390 毫秒。
SQL Server 分析和编译时间:
CPU 时间 = 0 毫秒,耗费时间 = 0 毫秒。
SQL Server 执行时间:
CPU 时间 = 0 毫秒,耗费时间 = 0 毫秒。
SQL Server 分析和编译时间:
CPU 时间 = 0 毫秒,耗费时间 = 0 毫秒。
说明:逻辑读取的数值十分接近 数据库中数据页数字,预读的次数也十分接近数据页的数字,物理读取值为0,即所需要查询的数据全部在预读的数据中间。
三、表添加主键,我们看一下有索引的查询
1. 此表的碎分布信息,从下图中可以看出此表的有非常多的内部碎片与外部碎片。
a) 此表的平均页密度只有97%,也就是说数据差不多把一个数据页都塞满了,没有多余的空间,没