本文出处:http://www.cnblogs.com/wy123/p/6770258.html
统计信息写过几篇了相关的文章了,感觉还是不过瘾,关于统计信息的问题,最近又踩坑了,该问题虽然不算很常见,但也比较有意思。
相对SQL Server 2012,发现在新的SQL Server版本(2014,2016)中都有一些明显的变化,下文将对此进行粗浅的分析。
SQL Server 2012中(包括之前的版本),因表中数据变化,但统计信息尚未更新的情况下,对于直方图中没有覆盖到的谓词过滤时,sqlserver总是预估为1行
SQL Server 2014和 Server 2016中这种估算方式都有所变化,从表现看,对于对于没有覆盖到的谓词过滤的预估,每个版本都是不同的。
本文简单测试一下此种情况在SQL Server 2012,SQL Server 2014,SQL Server 2016的不同表现,以及该问题可能造成的潜在影响。
下面涉及到的测试环境的数据库版本如下
测试环境准备
首先利用如下脚本,建一张测试表,写入测试数据,下面会解释测试数据的分布
create table A
(
IdentifierId int identity(1,1),
Id1 int,
Id2 int,
OtherCol CHAR(500)
)
GO
begin tran
declare @i int = 1
while @i<=1000000
begin
insert into A values ((@i/50000)+1,@i,NEWID())
set @i = @i+1
if (@i%500000)=0
begin
if @@TRANCOUNT>0
begin
commit
begin tran
end
end
end
if @@TRANCOUNT>0
begin
commit
end
GO
插入的测试数据的分布如下,Id1是从1~20,每一个Id1对应50000个不同的Id2
因为统计信息没有更新,在idx_1的直方图中,是没有Id1=50的信息的,也就说Id1=50不存在于统计信息的直方图中,
在SQL Server 2012中预估的结果:预估为1行,实际为50000行
重复以上测试代码,分别在SQL Server 2014和SQL Server 2016中测试,不重复截图了
SQL Server 2014中测试如下:行预估为1024.7,实际为50000,
这个值是通过什么方式计算出来的?暂时还没查到资料。
可以确定的是,对于类似情况的预估算法,也就是谓词没有包含在统计信息直方图中的情况下(one specifies a value which is out of range of the current statistics)
在sqlserver 2014中,经测试,不同情况下预估是不一样的,不是固定的预估为1行,也不是固定预估为的0.1%,也不是简单的Rows Sampled*All density
SQL Server 2016中测试如下: 预估为49880.8,实际为50000,基本上接近于真实值。
相对于SQL Server 2012和2014的预估结果,这个预估的准确性看起来还是比较吊的。
为什么SQL Server 2016中预估的如此准确?
因为在SQL Server 2016中,对于直方图中不存在的过滤谓词,在用这个谓词进行查询的时候,会自动更新相关的统计信息,然后再执行查询,
这个特性,相对于SQL Server 2012和2014来说,是全新的,也是非常实用的。
SQL Server 2014这个预估策略虽然在2012的基础上做出了一些改进,但是还是没有解决本质问题,以至于人仍旧要人为地干预统计信息的更新。
在SQL Server 2016中,即便是当前表中改变的数据行还没有达到触统计信息更新阈值的条件(传统上所谓的阈值,500+rowcount*20%),
统计信息依然会在查询的驱动下更新,通过索引上的统计信息可以看到,参考下图,直方图中生成了一个50的统计。
下面就是所谓触发统计信息更新阈值的条件(严格说是该规则仅对SQL Server 2016之前的版本有效,不适应于SQL Server 2016)
1,表格从没有数据变成有大于等于1条数据。
2,对于数据量小于500行的表格,当统计信息的第一个字段数据累计变化量大于500以后。
3,对于数据量大于500行的表格,当统计信息的第一个字段数据累计变化量大于500 + (20%×表格数据总量)以后。
这个说法,对于SQL Server 2016之前的版本是有效的,对于SQL Server 2016之后的版本是不成立的,我想这个还是值得注意的。
SQL Server 2016中统计信息更新策略相当于之前版本中开启了TraceFlag 2371,参考http://www.cnblogs.com/wy123/p/5748933.html
也即决定统计信息的变化值为动态的,不再拘泥于“数据累计变化量大于500 + (20%×表格数据总量)”这一限制。
除此之外,应该还要其他机制,比如这里的查询所触发的。 造成的问题
为什么微软会在SQL Server 2016中将统计信息的更新策略做出如此的改变,以及为什么笔者会来探究这个问题?
当然在实际业务中被这个问题坑的蛋疼。
问题很明显,类似于测试的场景,在SQL Server 2012(包括之前的版本),这种预估策略存在的严重的缺陷。
比如示例中:
因为没有当前过滤谓词的统计信息(或者说没有收集到当前谓词的统计信息),实际为5000行的情况下,预估为1行。
这种预估策略非常离谱,某种情况下会造成严重的性能问题,估计也很容易猜到,只是遇到的比较少罢了.
下面就简单具体说明,会造成什么问题,以及原因。
上述问题在什么情况下会造成性能问题,以及影响又多严重,这里仅简单举例说明。下面这个测试是在SQL Server 2012下进行的。
为演示这个问题,先来做另外一张测试表B,并写入测试数据。
create table B
(
IdentifierId int identity(1,1),
Id2 int,
OtherCol char(500)
)
GO
begin tran
declare @i int = 1
while @i<=1000000
begin
insert into B values (@i,NEWID())
set @i = @i+1
if (@i%100000)=0
begin
if @@TRANCOUNT>0
begin
commit
begin tran
end
end
end
if @@TRANCOUNT>0
begin
commit
end
GO
create index idx_2 on B(Id2)
GO
借助第二张表做一个测试,从而把错误预估行数造成的缺陷给放大,
执行下面两个SQL,分别查询A.Id1 = 5和A.Id1 = 50的信息,
由数据分布可知,查询总的结果总数会完全一样(截图受影响行数),
虽然A.Id1 = 5和A.Id1 = 50的数据量和分布也完全一样,但是后者的逻辑IO远远超出前者。
就是因为直方图中没有A.Id1 = 50的统计信息,A.Id1 = 50被错误地预估为1行造成的。