SQL Server 统计信息更新时采样百分比对数据预估准确性的影响

  

为何要写统计信息数据库

  最近看到园子里有人写统计信息,楼主也来凑热闹。
  话说常常作数据库的,尤为是作开发的或者优化的,统计信息形成的性能问题应该说是司空见惯。
  固然解决办法也并不是一成不变,“一招鲜吃遍天”的作法已经行不通了(题外话:整个时代不都是这样子吗)
  固然,仍是那句话,既然写了就不能太俗套,写点不同的,本文经过分析一个相似实际案例来解读统计信息的更新的相关问题。
  对于实际问题,不但要解决问题,更重要的是要从理论上深刻分析,才能更好地驾驭数据库。
 服务器

统计信息基础网络

首先说一个老掉牙的话题,统计信息的更新阈值:
1,表格从没有数据变成有大于等于1条数据。
2,对于数据量小于500行的表格,当统计信息的第一个字段数据累计变化量大于500之后。
3,对于数据量大于500行的表格,当统计信息的第一个字段数据累计变化量大于500 + (20%×表格数据总量)之后。性能

作个查询,触发统计信息更新,rowmodct归0(继续累积直到下一个触发的阈值,触发更新以后再次归0)测试

 

关于统计信息“过时”的问题优化

下面开始正文,网络上不少关于统计信息的文章,提到统计信息,不少都是统计信息过时的问题,而后跟新以后怎么怎么样
尤为在触发统计信息自动更新阈值的第三个区间:也就是说数据累计变化超过20%以后才能自动触发统计信息的更新
这一点对于大表来讲一般影响是比较大的,好比1000W的表,变化超过20%也+500也就是200W+500行以后才触发统计信息更新,
这个阈值区间的自动触发阈值,绝大多数状况是不能接受的,因而对于统计信息的诊断就变成了是否“过时”spa

 

 

判断统计信息是否过时,而后经过更新统计信息来促使执行计划更加准确地预估行数,这一点本无可厚非
可是,问题也就出在这里了:那么怎么更新统计信息?一成不变的作法是否可行,这才是问题的重点。
固然确定有人说,我就是按照默认方式更新的,更新完以后SQL也变得更加优化了什么的
经过update statistics TableName StatisticName更新某一个索引的统计信息,
或者update statistics TableName更新全表的统计信息
这种状况下每每是小表上能够这么作,固然对于大表或者小表没有一个标准值,一切要结合事实来讲明问题
 code

下面开始本文的主题:blog

抽象并简化出业务中的一个实际案例,建立这么一张表,相似于订单和订单明细表(主子表),
这里你能够想象成是一个订单表的子表,Id字段是惟一的,有一个ParentID字段,是非惟一的,
ParentID相似于主表的Id,测试数据按照一个主表Id对应50条子代表细的规律插入数据索引

CREATE TABLE [dbo].[TestStaitisticsSample](
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [ParentId] [int] NULL,
    [OtherColumn] [varchar](50) NULL
) 


declare @i int=0
while(@i<100000000)
begin

    insert into [TestStaitisticsSample](ParentId,OtherColumn)values(@i,NEWID())
    /*
    中间插入50条,也即一个主表Id对应50条子代表细
    */
    insert into [TestStaitisticsSample](ParentId,OtherColumn)values(@i,NEWID())

    set @i=@i+1
end
go

create nonclustered index [idx_ParentId] ON [dbo].[TestStaitisticsSample]
(
    [ParentId] 
)
go

 

原本打算插入1亿条的,中间我让他执行我睡午觉去了,醒来以后发现SSMS挂掉了,挂掉了算了,数据也接近1亿了,能说明问题就够了
如今数据分布的很是明确,就是一个ParentId有50条数据,这一点首先要澄清。

 

测试数据写入,以及所建立完成以后来更新 idx_ParentId 索引上的统计信息,就按照默认的方式来更新,而后来观察统计信息

 

默认方式更新统计信息(未指定采样密度)

表里如今是8000W多一点记录,默认更新统计信息时取样行数是462239行,那么这个统计信息靠谱吗?

上面说了,造数据的时候,我一个ParentId对应的是50行记录,这一点很是明确,他这里统计出来的多少?

1,对于取样的RANG_HI_Key值,好比51632,预估了862.212行

2,对于AVG_RANG_ROW,好比45189到51632之间的每一个Id的数据对应的数据行,预估是6682.490行

以前造数据的时候每一个Id都是50行,这里的预估靠谱吗,这个偏差是没法接受的,

不少时候,对于大表,采用默认(未指定采样密度)的状况下,默认的采样密度并不足以准确地描述数据分布状况

 

指定一个采样密度的方式更新统计信息(20%采样)

 

这一次用20%的采样密度,能够看到取样的行数是15898626行

1,对于取样的RANG_HI_Key值,好比216305,他给我预估了24.9295行

2,对于AVG_RANG_ROW,好比186302到216305之间的每一个Id的行数,预估是197.4439行

观察好比上面默认的取样密度,这一次不论是RANG_HI_Key仍是AVG_RANG_ROW得预估,都有不一个很是高的降低,开始趋于接近于真实的数据分布(每一个Id有50行数据)

总体上看,可是这个偏差仍是比较大的,若是继续提升采样密度,看看有什么变化?

 

指定一个采样密度的方式更新统计信息(70%采样) 

 

这一次用70%的采样密度,能够看到取样行数是55962290行

1,对于取样的RANG_HI_Key值,好比1978668,预估了71.15906行

2,对于AVG_RANG_ROW,好比1124024到1978668之间的每一个Id,预估为61.89334行

能够说,对于绝大多数值得预估(AVG_RANG_ROW),都愈发接近于真实值

   

 

指定一个采样密度的方式更新统计信息(100%采样)

 

能够看到,取样行数等于总行数,也就是所谓的所有(100%)取样

看一下预估结果:

好比Id=3981622,预估是50行,3981622与4131988之间的Id的行数,预估为49.99874行,基本上等于真实数据分布

这个就不作过多解释了,基本上跟真实值是同样的,只是AVG_RANG_ROW有一点很是很是小的偏差。

 

 取样密度高低与统计信息准确性的关系

 

  至于为何默认取样密度和较低取样密度状况下,偏差很大的状况我简单解释一下,也很是容易理解,
  由于“子表”中存储主表ID的ParentId值容许重复,在存在重复值的状况下,若是采样密度不够,极有可能形成“以偏概全”的状况
  好比对10W行数据取样1W行,本来10W行数剧中有2000个不重复的ParentId值,
  若是是10%的取样,在1W行取样数据中,由于密度不够大,只找到了20个不重复的ParentId值,
  那么就会认为每一行ParentId对应500行数据,这根实际的分布的每一个ParentId有一个很是大的偏差范围
  若是提升采样密度,那么这个偏差就会愈来愈小。
  

更新统计信息的时候,高比例的取样是否可取(可行) 

  所以在观察统计信息是否过时,决定更新统计信息的时候,必定要注意取样的密度,
  就是说表中有多少行数据,统计信息更新的时候取了多少采样行,密度有多高。
  固然,确定有人质疑,那你说采样密度越高,也就是取样行数越高越准确,那么我就100%取样。
  这样行不行?
  还要分状况看,对于几百万或者十几万的小表来讲,固然没有问题,这也是为何数据库越小,表数据越少越容易掩盖问题的缘由。
  对于大表,上亿的,甚至是十几亿的,你按照100%采样试一试?
  

  举个实际例子:

  我这里对一个稍微大一点的表作个全表统计信息的更新,测试环境,服务器没负载,存储是比普通的机械硬盘要强不少的SAN存储
  采用full scan,也就是100%采样的更新操做,看一下,仅仅这同样表的update statistic操做就花费了51分钟
  试想一下,对一个数百GB甚至数TB的库来讲,你敢这么搞一下。

  

 

  扯一句,这个中秋节过的,折腾了大半天,话说作测试过程当中电脑有开始有点卡,

  作完测试以后停掉SQLServer服务,瞬间内存释放了7个G,可见这些个操做仍是比较耗内存的

  

  

 

总结:

  本文经过对于某些场景下,在对较大的表的索引统计信息更新时,采样密度的分析,阐述了不一样采样密度下,对统计信息预估的准确性的影响。

  固然对于小表,一些都好说。

  随着单表数据量的增长,统计信息的更新策略也要作相应的调整,

  不光要看统计信息是否“过时”,更重要的是注意统计信息更新时究竟取样了全表的多少行数据作统计。

  对于大表,采用FULL SCAN或者100%采样每每是不可行的,这时候就须要作出权衡,作到既能准确地预估,又可以以合理的代价执行。

相关文章
相关标签/搜索