基数评估
目录
说明
查询优化器的目的是为了找出有效的执行计划,根据cost运算,取出cost最小的计划,作为执行计划。其中影响cost最重要的一项就是基数评估(估计行数)。SQL Server 2014对基数评估做了修改。
基数评估准确的重要性
基数评估提供以下信息:
1.响应行数评估(the distribution of data)
2.不同值个数评估(distinct value count)
3.重复值个数,作为上一级基数评估(duplicate count as input for parent operator estimation calculations)
基数评估是通过计算统计信息的出来的结果,而统计信息通过优化器创建或者通过索引创建。
统计信息分为:头,密度向量,直方图。
当统计信息存在的时候基数评估器使用密度向量和直方图来计算评估。
基数评估主要回答以下几个问题:
1.一个或多个谓词或过滤几行
2.2个表之间的连接谓词会过滤几行
3.预计一个指定列集合中有多少不同值(distinct value)
Sql server中有2种谓词:1.过滤谓词,2.连接谓词
基数评估(CE):试图回答where,join,having这些谓词的选择性。也试图回答group,distinct的不同值(distinct value)。
CE的计算从图形执行计划中是从右到左的,下一级的评估作为上一级计算评估的输入。
每个执行计划中的运算符都有评估值输入,这个值决定了优化器使用什么算法的操作符,同时也决定了最终的执行计划。所以如果评估出现偏差,会导致执行计划选择出现偏差,导致无法选出一个高效的执行计划。
评估出现偏差会出现以下结果:
如果评估过小:
1.原本可以使用并行计划更加有效的,现在使用串行计划
2.不合适的join算法
3.不合适的索引选择,和索引访问方法
如果评估过大:
1.原本使用串行计划更加有效,现在使用并行计划
2.不可合适的join算法
3.不合适的索引选择,和索引访问方法
4.过多的内存分配
5.内存浪费和没必要的并发
模型假设
内核有以下假设:
Independence:假设,在没有额外的相关信息之外,数据在不同的列是没有关联的
Uniformity:在统计信息的直方图的step,数据分布式均匀分布在step上的。
Containment: 2个表连接,那么高密度的一定被低密度的包含。
Inclusion:如果对一列对常数过滤,那么认为这个常数数据一定存在在这个列中。
启用新的基数评估
当数据库的兼容级别为120的时候,就是启用了新的基数评估,默认使用新的基数评估。
但是可以通过查询跟踪标记来指定:
2312:在兼容级别低于120的时候使用新的基数评估
9481:在兼容级别在120下,使用老的基数评估
验证基数评估的版本
可以从图形执行计划或者XML执行计划中找到CardinalityEstimationModelVersion,如果为120就是新的基数评估,70就是老的基数评估。
在迁移到新的基数评估前要测试
新的基数评估虽然总体提示了性能,但是对个别查询来说,会被影响,性能变差,所以要测试。
1.在类似生产环境下,测试大多数的负荷
2.可以先迁移到sql server 2014,但是使用不运行在120兼容级别
3.也可以到120兼容级别,但是在全局范围开9481跟踪标记
4.新建数据库推荐使用默认会使用120兼容级别。
校验基数评估
没有什么特别的就是通过实际值和评估值对个对比。
偏差问题
评估值偏差,是存在的,那么多少算是偏差太大了?其实没有一个固定的值,主要是看以下2点:
1.偏差是不是造成了资源过度使用
2.偏差是不是造成了特定查询的性能问题
如果任意一个出现问题的话,那么就能认为偏差太大了。
需要手动处理的变化
只有评估值变化的情况下,看性能是否下降超过预期,如果超过要进行手动干预。
如果评估值和老CE一样,并且计划没有什么变化,就不需要处理。
避免因为新的CE造成性能下降
1.能够从新基数评估得到性能优化的查询,就使用新基数评估,其他的进行重新调整。
2.有好处的查询使用新的基数评估,其他的使用跟踪标记9481
3.使用老的基数评估,特定的查询可以指定跟踪标记2312
4.直接调试有问题的sql
5.使用老的基数评估
SQL Server 2014中的修改
增加多个谓词的相关性的假设
在没有多列统计信息的情况下,SQL Server优化器会认为谓词之间是不相关的。
老的基数评估:各个谓词的选择度相乘
新的基数评估:选择度从低到高排序,然后使用以下公式:
修改超出统计信息范围的评估
如果超出统计信息范围,那么老的基数评估就认为不存在,评估行数为1。
新的基数评估会用,密度*总行数来当评估。
Join评估算法修改
简单Join
老的基数评估是以线性增长的方式一步一步对齐2个直方图。(根本不知道是怎么玩的)
新的基数评估,使用相对简单的join评估算法,只是用直方图的最大最小边界来对齐。(文章并没有给出详细的算法很坑爹)。
新的基数评估是用这种原则,很容易发现评估值不够准确。
多Join条件
多个join条件,对于老的基数评估来说,是独立的谓词,是用选择度相乘的方法来组合。
新的基数评估,是用2个不同值个数(distinct value count)中较小的一个,然后乘以2边的平均频率。(搞不懂)
Join带相等和不相等的谓词
老的基数评估,是独立的谓词,是用选择度相乘的方法来组合。
新的基数评估,认为大表小标多对1的关系。即大表中的一行,必定存在于表的一样与之对应。这个算法把大表的评估作为评估。(这个简单)
Join包含(Containment)假设的修改
如果是等值连接,那么就会假设这个列表2边都是存在的。如果存在join表上有非join谓词,老的基数评估那么会认为一些级别的相关,这种相关叫做简单包含(Simple Containment)。
老的基数评估的JOIN评估,假设在使用join谓词之前,任意存在的谓词会缩小直方图,而谓词之间是不相关的。老CE用这样的评估方式会让评估值偏大。
USE [AdventureWorks2012];
GO
SELECT [od].[SalesOrderID], [od].[SalesOrderDetailID]
FROM Sales.[SalesOrderDetail] AS [od]
INNER JOIN Production.[Product] AS [p]
ON [od].[ProductID] = [p].[ProductID]
WHERE [p].[Color] = 'Red' AND
[od].[ModifiedDate] = '2008-06-29 00:00:00.000'
OPTION (QUERYTRACEON 9481); -- CardinalityEstimationModelVersion 70
新的基数评估是使用基本包含(Base Containment),新的基数评估,是直接从基表上面获取选择度,而不是经过谓词过滤之后。
不同值计数评估的变化
对于新的基数评估和老的相比在多对多连接中,不同值计数评估相差很小。如果join条件会放大基数,老的基数评估可能会不准确。
新的基数评估根据join谓词和非join谓词选择不同值。新的基数评估使用环境基数(ambient cardinality),环境基数是group by或者distinct列的最小不同值集合(The new CE uses “ambient cardinality”, which is the cardinality of the smallest set of joins that contains the GROUP BY or DISTINCT columns.)。
诊断输出
使用新的xevent,query_optimizer_estimate_cardinality来输出
CREATE EVENT SESSION [CardinalityEstimate] ON SERVER
ADD EVENT sqlserver.query_optimizer_estimate_cardinality
ADD TARGET package0.event_file( SET filename = N'S:\CE\CE_Data.xel' ,
max_rollover_files =( 2 ) )
WITH ( MAX_MEMORY = 4096 KB ,
EVENT_RETENTION_MODE = ALLOW_SINGLE_EVENT_LOSS ,
MAX_DISPATCH_LATENCY = 30 SECONDS ,
STARTUP_STATE = OFF );
GO
-- Start the session
ALTER EVENT SESSION [CardinalityEstimate] ON SERVER STATE=START;
--
-- Your workload to be analyzed executed here (or in another session)
--
-- Stop the session after the workload is executed
ALTER EVENT SESSION [CardinalityEstimate] ON SERVER STATE=STOP;
也可以使用A first look at the query_optimizer_estimate_cardinality XE event中的方法。
新基数评估的调优方法
修改数据库兼容级别
如果新的CE出现性能问题,可以通过修改兼容级别回到以前的CE版本。
使用跟踪标记
可以使用QUERYTRACEON来使用查询级别选项,这个跟踪标记会影响一个语句的CE级别。
基础调优方法
统计信息丢失
统计信息丢失,但是没有及时创建,应该查看是否启用了统计信息自动创建选项。
统计信息过期
统计信息可能太老,导致查询性能问题,考虑是否要手动跟新统计信息。
统计信息采样率
表如果数据分布不均衡,无法在直方图上表现,看看统计信息头中的rows sampled和rows的差距,可以考虑使用调整采样率或者直接fullscan。
过滤统计信息(filtered statistics)
对于大的表存在数据不均衡,过滤统计信息可以很好的解决这个问题。
注意点:过滤统计信息的更新阀值是基于表的,而不是过滤谓词。
在没有recompile提示之下,过滤索引和过滤统计信息不会被应用到参数化的字段过滤。(In the absence of a RECOMPILE hint, filtered indexes and statistics will not be used in conjunction with parameterization that refers to the filter column.)。
多列统计信息
自动创建统计信息,只会创建单列的,多列统计信息是手工创建的,创建多列统计信息可以提高评估的准确性。提高查询性能。
参数敏感性(Parameter Sensitivity)
参数敏感性是因为数据分布不均衡,参数化的执行计划会以第一次运行的时候的参数来创建,如果数据不均衡,参数变化,会导致执行计划不是很理想的状况。可以加上OPTIMIZE FOR或者RECOPILE来解决。
表变量
在SQL Server 2014上面表变量没有相关的统计信息的,如果要在表变量上存大量的数据建议使用临时表。
多语句用户定义表变量函数
和表变量差不多,多语句表变量函数也没有统计信息,只是用一个固定的基数100。考虑使用内联表变量函数。
内联表变量函数式没有函数体的,直接return 一个表
而多语句表变量函数式有函数体的。
XML Reader表变量函数操作
使用nodes() XQUERY方法如果没有XML索引,会导致很烂的评估,因为XML索引可以带统计信息。解决这个问题的方法是在XML列上加上XML索引。
数据类型转化
如果谓词数据类型,不对会导致数据类型转化,这样也会导致基数评估不准确,是因为列的统计信息无法使用了。
Intra-column比较
Cardinality estimate issues can occur when performing comparisons between columns in the same table. If this is an issue, consider creating computed columns. The computed column can then be referenced in a filter predicate. You can automatically generate the computed column’s associated statistics (by activating automatic statistics creation at the database level) to improve overall estimates. If intra-table column comparison cardinality estimation is a frequent issue, consider normalization techniques (separate tables), derived tables, or common table expressions.
查询提示
尽量避免使用查询提示,查询提示会覆盖优化器的选择,对于某些情况可能会导致更烂的性能。
分布式查询
当通过连接服务器查询数据的时候,如果没有权限访问统计信息的话,会导致查询性能很烂。
在使用前先确定访问统计信息需要多大的权限。
递归CTE
递归CTE也有评估偏差问题,可以看Optimize Recursive CTE Query.
谓词复杂性
避免在谓词列上使用函数或者表达式,这个没啥好说,严重影响性能。
查询复制性
复制的查询也会让评估带来偏差,评估本来就是不准确的,来回怎么倒腾就更加不准确了,应该简化查询。
参考:
Optimizing Your Query Plans with the SQL Server 2014 Cardinality Estimator