使用过滤统计信息解决基数预估错误

基数预估是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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/258661.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

faster-rcnn系列学习之准备数据

如下列举了 将数据集做成VOC2007格式用于Faster-RCNN训练的相关链接。 RCNN系列实验的PASCAL VOC数据集格式设置 制作VOC2007数据集用于Faster-RCNN训练 将数据集做成VOC2007格式用于Faster-RCNN训练 这一篇比较详细地介绍了如何制造voc2007的所有文件&#xff0c;内含相关软件…

C# 委托链、多路广播委托

委托链、多路广播委托&#xff1a;也就是把多个委托链接在一起,我们把链接了多个方法的委托称为委托链或多路广播委托 例&#xff1a; 1 class HelloWorld2 {3 //定义委托类型4 delegate void DelegationChain();5 static void Main(string[] args)6 …

openssl 生成证书_使用证书和私钥导出P12格式个人证书!

【OpenSSL】使用证书和私钥导出P12格式个人证书1, 产生CA证书1.1, 生成ca的私钥openssl genrsa -out cakey.pem 20481.2, 生成ca的自签名证书请求openssl req -new -key cakey.pem -subj "/CNExample Root CA" -out cacsr.pem1.3, 自签名ca的证书openssl x509 -req -…

PHP (20140505)

数据库表与表之间的连接是用id联系。 join on&#xff1b;转载于:https://www.cnblogs.com/sunshine-c/p/3710283.html

py-faster-rcnn代码roidb.py的解读

roidb是比较复杂的数据结构&#xff0c;存放了数据集的roi信息。原始的roidb来自数据集&#xff0c;在trian.py的get_training_roidb(imdb)函数进行了水平翻转扩充数量&#xff0c;然后prepare_roidb(imdb)【定义在roidb.py】为roidb添加了一些说明性的属性。 在这里暂时记录下…

python 概率分布_python实现概率分布

伯努利分布from scipy import statsimport numpy as npimport matplotlib.pyplot as pltxnp.arange(0,2,1)xarray([0, 1])# 求对应分布的概率&#xff1a;概率质量函数 (PMF)p0.5# 硬币朝上的概率dfstats.bernoulli.pmf(x,p)dfarray([0.5, 0.5])#绘图vlines用于绘制竖直线(vert…

CodeForces 7D Palindrome Degree 字符串hash

题目链接&#xff1a;点击打开链接 #include<stdio.h> #include<iostream> #include<string.h> #include<set> #include<vector> #include<map> #include<math.h> #include<queue> #include<string> #include<stdlib…

程序清单8-9 回送所有命令行参数和所有环境字符串

1 /*2 3 Name : test.c4 Author : blank5 Version :6 Copyright : Your copyright notice7 Description : 程序清单8-9 回送所有命令行参数和所有环境字符串8 9 */ 10 11 #include "ourhdr.h" 12 13 int main(int argc, char *argv[]) 14…

SQL快速入门

关系化数据库保存关系模式数据的容器关系模式是对业务对象实体&#xff0c;属性以及关系的抽象&#xff0c;提炼需求的名词是建立实体关系模型常用的方法。要了解E-R实体关系图的绘制。常用关系数据库Microsoft SQL Server&#xff1b;微软公司产品&#xff0c;中等规模数据库&…

Faster RCNN minibatch.py解读

minibatch.py 的功能是&#xff1a; Compute minibatch blobs for training a Fast R-CNN network. 与roidb不同的是&#xff0c; minibatch中存储的并不是完整的整张图像图像&#xff0c;而是从图像经过转换后得到的四维blob以及从图像中截取的proposals&#xff0c;以及与之对…

oracle精简版_使用Entity Framework Core访问数据库(Oracle篇)

前言哇。。看看时间 真的很久很久没写博客了 将近一年了。最近一直在忙各种家中事务和公司的新框架 终于抽出时间来更新一波了。本篇主要讲一下关于Entity Framework Core访问oracle数据库的采坑。。强调一下&#xff0c;本篇文章发布之前 关于Entity Framework Core访问oracl…

interrupt、interrupted 、isInterrupted 区别

interrupt&#xff1a;调用方法&#xff0c;是线程处于中断状态&#xff0c;但是这个方法只是让线程设置为中断状态&#xff0c;并不会真正的停止线程。支持线程中断的方法就是在坚持线程中断状态&#xff0c;一旦线程中断状态被设置为中断&#xff0c;就会抛出异常。interrupt…

java String部分源码解析

String类型的成员变量 /** String的属性值 */ private final char value[];/** The offset is the first index of the storage that is used. *//**数组被使用的开始位置**/private final int offset;/** The count is the number of characters in the String. *//**String中…

python在材料模拟中的应用_基于Python的ABAQUS二次开发及在板料快速冲压成形模拟中的应用...

2009doi:1013969/j1issn1100722012120091041013基于Python的ABAQUS二次开发及在板料快速冲压成形模拟中的应用(北京航空航天大学飞行器制造工程系,北京100191)吴向东刘志刚万敏王文平黄霖摘要:采用Python脚本语言对ABAQUS的前处理模块进行二次开发,讨论了Python脚本在ABAQUS二次…

Doxygen简介

&#xff08;转自&#xff1a;http://www.cnblogs.com/liuliunumberone/archive/2012/04/10/2441391.html&#xff09; 一&#xff0e;什么是Doxygen? Doxygen 是一个程序的文件产生工具&#xff0c;可将程序中的特定批注转换成为说明文件。通常我们在写程序时&#xff0c;或多…

javascript之闭包理解以及应用场景

1 function fn(){2 var a 0;3 return function (){4 return a;5 } 6 }如上所示&#xff0c;上面第一个return返回的就是一个闭包&#xff0c;那么本质上说闭包就是一个函数。那么返回这个函数有什么用呢&#xff1f;那是因为这个函数可以调用到它外部的a…

faster rcnn学习之rpn、fast rcnn数据准备说明

在上文《 faster-rcnn系列学习之准备数据》,我们已经介绍了imdb与roidb的一些情况&#xff0c;下面我们准备再继续说一下rpn阶段和fast rcnn阶段的数据准备整个处理流程。 由于这两个阶段的数据准备有些重合&#xff0c;所以放在一起说明。 我们并行地从train_rpn与train_fas…

sql server规范

常见的字段类型选择 1.字符类型建议采用varchar/nvarchar数据类型2.金额货币建议采用money数据类型3.科学计数建议采用numeric数据类型4.自增长标识建议采用bigint数据类型 (数据量一大&#xff0c;用int类型就装不下&#xff0c;那以后改造就麻烦了)5.时间类型建议采用为dat…

关于标准库中的ptr_fun/binary_function/bind1st/bind2nd

http://www.cnblogs.com/shootingstars/archive/2008/11/14/860042.html 以前使用bind1st以及bind2nd很少&#xff0c;后来发现这两个函数还挺好玩的&#xff0c;于是关心上了。在C Primer对于bind函数的描述如下&#xff1a;“绑定器binder通过把二元函数对象的一个实参绑定到…

CSS伪类

一、首字母的颜色字体写法 p:first-letter 二、文本的特殊样式设置 first-line css伪类可与css类配合使用 伪元素只能用于块级元素 转载于:https://www.cnblogs.com/boyblog/p/4623374.html