使用过滤统计信息解决基数预估错误
基数预估是SQL Server里一颗隐藏的宝石。一般而言,基数预估指的是,在查询编译期间,查询优化器尝试找出在执行计划里从各个运算符平均返回的行数。这个估计用来驱动计划本身生成并选择正确的计划运算符——例如像Nested Loop, Merge Join,还是Hash Join的物理连接。当这些估计错误时,查询优化器就会选择错误的计划运算符,相信我——你的查询就会非常非常非常慢!
查询优化器使用称为统计信息对象作为基数预估。每次当你创建一个索引,SQL Server在下面也会创建一个统计对象。这个对象描述了那个索引的数据分布。另外,在查询执行时,SQL Server也能创建统计信息对象,在必须的时候(自动创建统计信息)。数据分布本身(复合索引键的第一列)被描述为所谓的直方图(Histogram)。
直方图最痛苦之一就是最大只有200的步长。步长是对于你所给定列数据一部分的数据分布情况描述。你的表变得越大,你的直方图就越不准确,因为你有最大200的步长(直方图必须尽可能紧凑,它必须复核8kb的页)。
在复合索引键里其他列,SQL Server在统计信息对象里用所谓的密度向量(Density Vector)来保存,它是复合索引键唯一值是如何的情况描述(彼此结合在一起)。例如在某列里有3个不同值,那列的密度向量是0.33333(1/3)。
从SQL Server 2008开始,SQL Server支持所谓的过滤统计信息(Filtered Statistics)(和过滤索引对应)。使用过滤统计信息,你可以为数据的子集创建统计信息对象。对于那个数据子集,你也会有直方图和密度向量。如果在你的数据里有极端值,你可以对那个范围的数据创建过滤统计信息对象,当那个范围的数据被查询时,就可以让查询优化器更好的估计返回的行数。因此使用过滤统计信息,你就提高了基数预估的准确性,SQL Server就会给更好的执行计划性能。下面代码显示在SQL Server 2008及后续版本里如何创建过滤统计信息对象:
1 CREATE STATISTICS Country_Austria ON Country(ID) 2 WHERE Name = 'Austria' 3 GO
从上面代码可以看到,你用WHERE子句限制表数据的子集,那会通过新的过滤统计信息对象来描述这些数据。但也只有的你的查询也包含这个where条件,查询优化器才可以只用这个新的统计信息对象,就像这样:
1 SELECT SalesAmount FROM Country 2 INNER JOIN Orders ON Country.ID = Orders.ID 3 WHERE Name = 'Austria' 4 GO
如果在的查询里并不包含同样的WHERE子句,查询优化期在执行计划里访问的索引的统计信息还是原来默认的。如果你对刚才的查询启用9204的跟踪标记,你就可以看到在基数预估时,那个统计信息被查询优化器使用:
1 SELECT SalesAmount FROM Country 2 INNER JOIN Orders ON Country.ID = Orders.ID 3 WHERE Name = 'Austria' 4 OPTION 5 ( 6 RECOMPILE,-- Used to see the Statistics Output 7 QUERYTRACEON 3604,-- Redirects the output to SSMS 8 QUERYTRACEON 9204 -- Returns the Statistics that were used during Cardinality Estimation ("Stats loaded") 9 ) 10 GO
查询本身也会编译(因为RECOMPLIE查询提示,即使查询计划已被缓存),因此在SSMS的消息窗,你就可以看到拿个统计信息被用做基数预估。
以过滤统计信息的简单介绍为基础,我想给你通过实例展示下,过滤统计信息是如何提高执行计划质量的。
1 -- Create a new database 2 CREATE DATABASE FilteredStatistics 3 GO 4 5 -- Use it 6 USE FilteredStatistics 7 GO 8 9 -- Create a new table 10 CREATE TABLE Country 11 ( 12 ID INT PRIMARY KEY, 13 Name VARCHAR(100) 14 ) 15 GO 16 17 -- Create a new table 18 CREATE TABLE Orders 19 ( 20 ID INT, 21 SalesAmount DECIMAL(18, 2) 22 ) 23 GO
我们在表上建立相应的索引:
1 -- Create a Non-Clustered Index 2 CREATE NONCLUSTERED INDEX idx_Name ON Country(Name) 3 GO 4 5 -- Create a Clustered Index 6 CREATE CLUSTERED INDEX idx_ID_SalesAmount ON Orders(ID, SalesAmount) 7 GO
最后往2个表里插入初始数据:
1 -- Insert a few records into the Lookup Table 2 INSERT INTO Country VALUES(0, 'Austria') 3 INSERT INTO Country VALUES(1, 'UK') 4 INSERT INTO Country VALUES(2, 'France') 5 GO 6 7 -- Insert uneven distributed order data 8 INSERT INTO Orders VALUES(0, 0) 9 10 DECLARE @i INT = 1 11 12 WHILE @i <= 1000 13 BEGIN 14 INSERT INTO Orders VALUES (1, @i) 15 SET @i += 1 16 END 17 GO
为了保证所有的统计信息都已经是最新的,我用全扫描更新了统计信息:
1 -- Update the Statistics on both tables 2 UPDATE STATISTICS Country WITH FULLSCAN 3 UPDATE STATISTICS Orders WITH FULLSCAN 4 GO
点击工具栏的显示包含实际的执行计划。我们来执行下列的查询:
1 SELECT SalesAmount FROM Country 2 INNER JOIN Orders ON Country.ID = Orders.ID 3 WHERE Name = 'UK' 4 OPTION 5 ( 6 RECOMPILE,-- Used to see the Statistics Output 7 QUERYTRACEON 3604,-- Redirects the output to SSMS 8 QUERYTRACEON 9204-- Returns the Statistics that were used during Cardinality Estimation ("Stats loaded") 9 ) 10 GO
从执行计划里可以看到,基数预估出现了大问题。
SQL Server 估计行数是501,聚集索引查找运算符的实际行数是1000。SQL Server这里使用idx_ID_SalesAmount统计信息对象的密度向量来做那个估计:密度向量是0.5(在那列我们只有2个不同值),因此估计行数是501(1001 * 0.5)。
当你用Austria参数值执行同样的查询,SQL Server又一次估计行数是501,但是查询本身值返回1行……当其他运算符使用这些估计做运算时,这个行为在执行计划里会有巨大的副作用。例如,Sort和Hash运算符根据这些估计作为内存授予需要的大小。如果低估,你的查询会涌向TempDb,如果高估,你就在浪费内存,当你有大量的并发查询是,就会导致竞争问题(查询内存的最大数量是有资源管理器限制的……)
你可以使用过滤统计信息来帮助这些特殊场景。这个会给SQL Server关于数据本身分布的更多信息,也会在基数预估里得到帮助。对于那个特殊场景,我创建2个不同的过滤统计信息,对于每个国家我都创建各自的过滤统计信息对象:
1 -- Fix the problem by creating Filtered Statistics Objects 2 CREATE STATISTICS Country_UK ON Country(ID) 3 WHERE Name = 'UK' 4 5 CREATE STATISTICS Country_Austria ON Country(ID) 6 WHERE Name = 'Austria' 7 GO
现在当你重新执行查询时,最后你会看到基数预估是正确的:
当你在你表上上创建了过滤统计信息时,你也要注意维护。从整个表本身——如果有20%的数据改变时,SQL Server会自动更新统计信息!!! 假设你有10000行的表,你在表的子集上创建了过滤统计信息,就定子集行数是500条。在这个情况下,当指定列有2000行改变时,SQL Server会更新过滤统计信息对象。因此你要更新过滤统计信息对象里4倍的数据,才会使统计信息失效然后它被更新(在过滤统计信息区间外,没有数据发生改变)。这是很糟糕的情况,当你使用过滤统计信息时,要记住这个。
希望这篇文章给你过滤统计信息的很好概述,对于给出的查询,你知道如何使用过滤统计信息帮助SQL Server提高基数预估。
感谢关注!
参考文章:
https://www.sqlpassion.at/archive/2013/10/29/fixing-cardinality-estimation-errors-with-filtered-statistics/
转载于:https://www.cnblogs.com/woodytu/p/4618818.html
使用过滤统计信息解决基数预估错误相关推荐
- 全废话SQL Server统计信息(2)——统计信息基础
接上文:http://blog.csdn.net/dba_huangzj/article/details/52835958 我想在大地上画满窗子,让所有习惯黑暗的眼睛都习惯光明--顾城<我是一个 ...
- 通过手动创建统计信息优化sql查询性能案例
本质原因在于:SQL Server 统计信息只包含复合索引的第一个列的信息,而不包含复合索引数据组合的信息 来源于工作中的一个实际问题, 这里是组合列数据不均匀导致查询无法预估数据行数,从而导致无法选 ...
- SQL Server中的筛选后的统计信息和CE模型变化
In this blog post, we are going to view some interesting model variation, that I've found while expl ...
- 理解统计信息(3/6):谁创建和管理统计信息?在性能调优中,统计信息的作用。...
在理解统计信息(2/6):直方图 中,我们讨论了直方图,密度向量,还有SQL Server如何用统计信息做基数计算(cardinality estimation).这篇文章会讨论统计信息如何被创建,还 ...
- SQL Server读懂语句运行的统计信息 SET STATISTICS TIME IO PROFILE ON
对于语句的运行,除了执行计划本身,还有一些其他因素要考虑,例如语句的编译时间.执行时间.做了多少次磁盘读等. 如果DBA能够把问题语句单独测试运行,可以在运行前打开下面这三个开关,收集语句运行的统计信 ...
- oracle数据泵导入分区表统计信息报错(一)
今天在进行数据泵导入操作时,发现一个bug. 数据库版本Oracle 10203 for Solaris RAC,执行导入在处理表的统计信息时报错,错误信息为:ORA-39083和ORA-917. 经 ...
- 如何检测过期的统计信息(转)
在大多数情况下,人为更新统计信息可以获得更好的性能.这个文章,我们可以来看下如何检测过期的统计信息. 在SQL Server 2005以后的版本里,SQL Server使用ColModCtr 对统计的 ...
- 第13/24周 统计信息
欢迎来到性能调优培训的第4个月.这个月全是关于SQL Server里的统计信息,还有它们如何帮助查询优化器生成足够好的执行计划.统计信息主要是被查询优化器用来估计查询返回的行数.它只是个估计,没别的. ...
- oracle导入表 忽略报错,oracle数据泵导入分区表统计信息报错(一)
今天在进行数据泵导入操作时,发现一个bug. 数据库版本Oracle 10203 for Solaris RAC,执行导入在处理表的统计信息时报错,错误信息为:ORA-39083和ORA-917. 经 ...
最新文章
- JQ_Web Uploader图片上传控件
- curl可以访问但httpclient不能访问_exta进程不能访问+ASM实例的解决方法
- 参数整定临界比例度实验_控制算法手记自动整定方法初步
- 学习笔记(11月03日)
- 顺序表Sqlist.cpp
- 【Nginx那些事】Nginx 配置文件说明
- python现有两个磁盘文件a和b_有两个磁盘文件a和b,各存放一行字母,今要求把这两个文件中的信息合并...
- 第 6 章 MybatisPlus 代码生成器
- 如何将html转为report,如何把Html5 Report Viewer添加到Web项目
- sql中毫秒数与格式化时间的转换
- 虚拟机下liunx安装jdk
- JavaScript简单计算器
- python为什么胶水语言_为什么称python为胶水语言
- 数据分析师面试题目_数据分析师常见的10道面试题解答
- Rosalind Java|k-Mer Composition
- 360安全卫士添加信任区
- 期货ios模拟交易软件
- 2021-6-7-今日收获
- 骁龙768G相当于什么处理器 骁龙780g什么水平
- 怎么撰写一份优秀的数据分析报告(二)
热门文章
- 神经网络激活函数对数函数_神经网络中的激活函数
- 无监督学习与监督学习_有监督与无监督学习
- 总体方差的充分统计量_R方是否衡量预测能力或统计充分性?
- 做一个幸福的“生活家”:谈《心欢喜,灵快乐》
- 说不尽的蒙古人:推荐多兰的诗歌
- MATLAB工具箱介绍
- 北斗通信运营商_国内首个“北斗+5G”应用方案发布 配套5G产品将于年底量产上市...
- 启动之后自己关闭_电脑一开都是广告,请问怎么永久关闭?
- 一款已上市MMO手游地图同步方案总结
- 【TensorFlow-windows】name_scope与variable_scope