自动更新统计信息的阀值——人为更新统计信息的重要性

本文涉及的产品
云数据库 RDS SQL Server,独享型 2核4GB
简介:

经过一系列的Insert/Update/Delete后,统计信息可能不会是最新。如果SQL Server查询优化器在表里需要指定列的统计信息,自上次统计信息创建或更新后经历了实质的更新活动,SQL Server会通过采样列值自动更新统计信息(通过自动更新统计信息)。统计信息的自动更新由查询优化器或编译好的计划执行来触发,它只涉及到查询里引用到的各个列。如果自动异步更新统计信息是停用的话,统计信息会在查询编译前更新,启用的话是在查询编译后更新。当统计信息是异步更新时,受益于触发更新的查询使用老的统计信息。对一些工作量来说,这可以提供更可预估的响应时间,尤其是那些大表上的短时间运行的查询。

当一个查询首次编译完成,如果优化器需要指定对象的统计信息,这个统计信息存在的话,若已过期则自动更新统计信息。如果一个查询被执行且它的计划在缓存里,计划依赖的统计信息会被检查是否过期,如果过期,计划会在缓冲中移除,在查询的重编译时,统计信息会被更新。如果计划依赖的任何统计信息被更新的话,计划都会从缓存中移除。

SQL Server 2008基于列修改的计数器(colmodctrs)来决定是否更新统计信息:

在下列情况下,统计信息对象被认为过期:

如果在常规表上定义的统计信息,被认为过期的话,那么:

  1. 表的大小从0行变成了大于0行(测试1)
  2. 当统计信息收集时,表的行数为500或更少,统计的第一列对象的计数器,自改变为大于500时(测试2)。
  3. 当统计信息收集时,表的行数大于500时,统计的第一列对象的计数器,受表里超过500 +20%的行数而改变(测试3)。

上述描述来自微软的MSDN,具体参见Statistics Used by the Query Optimizer in Microsoft SQL Server 2008

前2个条件还是相当好的,但第3个条件在处理大表时,有些时候阀值会很高,但对统计信息更新还是无效。例如有个表有100000条记录,只有在200500条件记录被修改后(update/insert),对于触发自动更新还是无效的阀值。

我们来看个例子。

复制代码
1 USE StatisticsDB
2 GO
3 
4 DROP TABLE SalesOrderDetail
5 SELECT * INTO SalesOrderDetail FROM AdventureWorks2008r2.sales.SalesOrderDetail
6 CREATE INDEX ix_ProductID ON SalesOrderDetail(ProductID)
7 SET STATISTICS IO ON
8 SELECT * FROM SalesOrderDetail WHERE ProductID=725
复制代码

我们创建了SalesOrderDetail表的副本,并在上面创建非聚集索引,我们看下最后SELECT查询的执行计划,点击工具栏的显示包含实际的执行计划。

 

优化器选择了索引查找和书签查找操作作为优化的计划,完成这个操作需要377个逻辑读。

salesOrderDetail 表有121317条记录,上述第3个条件如果要使统计信息无效的话,121317的20% =24263+500=24763条记录需要被修改,我们用下列语句只更新5000条记录,再次看看查询的执行计划,点击工具栏的显示包含实际的执行计划。

复制代码
1 SET ROWCOUNT 5000
2 UPDATE SalesOrderDetail SET ProductID=725 WHERE ProductID<>725
3 SET ROWCOUNT 0
4 SET STATISTICS IO ON
5 SELECT * FROM SalesOrderDetail WHERE ProductID=725
复制代码

执行计划里估计行数是374,这是基于上次更新操作收集的统计信息。优化器基于统计信息,选择了索引查找和书签查找作为最优计划。SELECT操作进行5390逻辑读来完成这个操作。

下一步,我们用producid值为725来更新19762条记录。实际上我们更新24762条记录(包含上一步5000条更新的记录),比使统计信息无效的更新的记录(24763)少1条。

复制代码
1 SET ROWCOUNT 19762
2 UPDATE SalesOrderDetail SET ProductID=725 WHERE ProductID<>725
3 SET ROWCOUNT 0
4 SET STATISTICS IO ON
5 SELECT * FROM SalesOrderDetail WHERE ProductID=725
复制代码

执行计划里估计行数是374,这是基于上次更新操作收集的统计信息。优化器基于统计信息,选择了索引查找和书签查找作为最优计划。完成这个操作需要25206个逻辑读。

现在我们更新再多一条记录使统计信息无效。

复制代码
1 SET ROWCOUNT 1
2 UPDATE SalesOrderDetail SET ProductID=725 WHERE ProductID<>725
3 SET ROWCOUNT 0
4 SET STATISTICS IO ON
5 SELECT * FROM SalesOrderDetail WHERE ProductID=725
复制代码

(这里我跌了个跟头,在SQL SERVER 2008R2里首次执行,始终是下列结果:

回家吃饭还在思考这个问题,一想原因,应该是自动创建统计信息和自动更新统计信息被停用的原因(上篇文章理解统计信息(3/6):谁创建和管理统计信息?在性能调优中,统计信息的作用 代码执行后未还原为默认设置),在数据库属性里一看,果然是False状态,赶紧用下列语句启用,出现的问题立马消失!

1 ALTER DATABASE StatisticsDB SET AUTO_CREATE_STATISTICS ON
2 ALTER DATABASE StatisticsDB SET AUTO_UPDATE_STATISTICS ON

看来计算机是最诚实可靠的,即使计算机犯了错,也是因为人犯错造成的! )

和我们预期的一样,SELECT语句触发了自动更新统计信息,计划中的估计行数和实际行数已经非常接近了。这可以帮助优化器选择更好的执行计划。优化器选择了表扫描而不是索引查找和书签查找。SELECT操作只进行了1495个逻辑读来选取25137条记录,比起25212个逻辑读才选择2516条记录。在第一步,我们只更新了5000条记录,如果统计信息在那个时候更新的话,优化器可能会选择表扫描作为最优计划而不是索引查找和书签查找。那样的话就可以只用1495个逻辑读代替5390个逻辑读来完成操作,这样就会好很多。

从这个例子我们可以清楚看到,对于自动更新统计信息的阀值对于获得最优性能还是不够好。对于大表来说会更糟。我们就需要人为去更新统计信息用来保证长须的最佳性能,当然更新的频率要看具体的工作量。

在进行大量DML操作后,统计信息都会过期,在查询计划访问统计信息前,统计信息都不会自动更新。更清楚的说,SQL Server会在下列情况自动更新统计信息:

  • 查询第一次编译,计划使用到的统计信息已经过期
  • 查询已有存在的查询计划,但计划中的统计信息已经过期。 

本文转自Woodytu博客园博客,原文链接:http://www.cnblogs.com/woodytu/p/4521590.html,如需转载请自行联系原作者
相关实践学习
使用SQL语句管理索引
本次实验主要介绍如何在RDS-SQLServer数据库中,使用SQL语句管理索引。
SQL Server on Linux入门教程
SQL Server数据库一直只提供Windows下的版本。2016年微软宣布推出可运行在Linux系统下的SQL Server数据库,该版本目前还是早期预览版本。本课程主要介绍SQLServer On Linux的基本知识。 相关的阿里云产品:云数据库RDS&nbsp;SQL Server版 RDS SQL Server不仅拥有高可用架构和任意时间点的数据恢复功能,强力支撑各种企业应用,同时也包含了微软的License费用,减少额外支出。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/sqlserver
相关文章
|
Oracle 关系型数据库
10G自动收集统计信息修改
10G自动收集统计信息修改
90 0
10G自动收集统计信息修改
|
存储 测试技术 开发工具
BSTestRunner增加历史执行记录展示和重试功能
之前对于用例的失败重试,和用例的历史测试记录存储展示做了很多的描述呢,但是都是基于各个项目呢,不方便使用,为了更好的使用,我们对这里进行抽离,抽离出来一个单独的模块,集成到BSTestRunner中,以后我们使用BSTestRunner直接就可以使用里面的失败重试和展示历史记录了。
BSTestRunner增加历史执行记录展示和重试功能