SQL Server SQL性能优化之--数据库在“简单”参数化模式下,自动参数化SQL带来的问题

数据库参数化的模式

数据库的参数化有两种方式,简单(simple)和强制(forced),默认的参数化默认是“简单”,
简单模式下,如果每次发过来的SQL,除非完全一样,否则就重编译它(特殊情况会自动参数化,正是本文想说的重点)
强制模式就是将adhoc SQL强制参数化,避免每次运行的时候因为参数值的不同而重编译,这里不详细说明。

这首先要感谢“潇湘隐者”大神的提示,
当时也是遇到一个实际问题,
发现执行计划对数据行的预估,怎么都不对,有观察到无论怎么改变参数,SQL语句执行前都没有重编译,疑惑了好一会,
这个问题正是简单参数化模式下,对某些SQL自动参数化造成执行计划重用引起的,也是本文想表达的重点。
这个问题之前就写过,当时也只是看书上理论上这么说的,没有想到其带来的影响

  该参数是一个数据级别的选项,设置情况可以参考下图

  

什么情况下会自动参数化

  简单参数化模式下,对于有且只有一种执行方式的Adhoc SQL语句,SQL Server会自动参数化它,从而达到重用执行计划的目的。
  究竟哪些类型的SQL会被自动参数化,后面会举例说明。

自动参数化会存在哪些问题

  在简单模式下,SQL对于某些SQL会自动参数化他,避免每次都重编译。
  SQL Server 自动参数化SQL语句的行为,能够避免一些重编译,原本也是出于“好意”,但是这种“好意”往往不一定总是给我们带来好处。

举例说明什么情况下会自动参数化

  先造一个简单的测试环境

create table TestAuotParameter
(
    id int not null,
    col2 varchar(50)
)
GO

declare @i int=0
while @i<100000
begin
    insert into TestAuotParameter values (@i, NEWID())
    set @i=@i+1
end
GO

create unique index idx_id on TestAuotParameter(id)
GO

之所以自动参数化了SQL语句,就是因为select * from TestAuotParameter where id=33333 (66666,99999)这句SQL语句,
在当前的数据量下和唯一索引的特点,决定了有且只有一种高效的执行方式(也就是索引查找)
这里说有且只有一种方式是表中数据量相对较多,又因为idx_id这个索引是unique的。如果不是unique的,那么情况就不同了

下面来解释什么是有且只有一种高效的执行计划

  如下截图:同样的测试,我删除id上的唯一索引,创建为非唯一索引,再做同样的测试,就会发现执行同样的SQL并没有被自动参数化

  这里解释一下原因,索引类型怎么跟执行计划缓存扯上了?
  对于非唯一索引,有可能作做引查找是高效的,有可能做全表扫描是高效的(比如某个ID的数据分布的特别多)
  此时执行计划有可能是多样的,不仅仅只有一种方式,所以就不会自动参数化SQL

自动参数化存在的问题

  自动参数化好处并不用多说,因为可以重用缓存的执行计划,避免了每次参数值不一样就重编译的问题
  说到执行计划重用,不得不说的一个话题就是parameter sniff,嘴皮子都磨破的问题了
  没错,自动参数化因为不同参数会重用第一次编译生成的执行计划,很有可能造成parameter sniff问题,以及parameter sniff衍生出来的其他问题

  

同样用一个例子来做演示,该问题是最近在观察执行计划统计信息(statistics)预估问题时遇到的一个问题,让我困惑了好一会,
这里再次感谢潇湘隐者。
该问题同样也是因为自动参数化了SQL语句,造成执行计划重用,从而造成一个极其简单的SQL执行效率在某些情况下较低的情况,
为什么自动化参数的原因跟上述类似,都是有且只有一种执行方式(索引查找)的情况下,不同参数执行计划重用造成对数据行的错误预估。

  

  测试之前清空一下缓存执行计划,观察不同查询条件下的实际执行计划对数据行的预估

  如下查询条件:

  1,初始查询条件为:CreateDate>‘2016-6-1‘ and CreateDate<‘2016-6-2‘,观察执行计划,实际行数是37903,预估行数为37117,预估还算准确  

 2,将查询条件更新为:CreateDate>‘2016-6-1‘ and CreateDate<‘2016-6-5‘,观察执行计划,实际行数为150706,预估行数不变,还是37117

 3,将查询条件更新为:CreateDate>‘2016-6-1‘ and CreateDate<‘2016-6-9‘,观察执行计划,实际行数为302114,预估行数不变,还是37117

  ,

  发现没有,因为查询时间段有变化,实际行数也有变化,但是不管实际行数多少,预估行数总是为第一次执行预估的行数。

这肯定不对吧?随便带入什么条件,预估行数都是37117,当时一下子蒙了,怎么每次执行SQL对数据行的预估都是一样的?
其实这个问题跟一开始举例的一样,都是SQL语句被自动参数化了,因此造成了执行计划重用,
执行计划重用,导致错误地预估实际查询的数据行数。

如何解决自动参数化造成错误地重用执行计划的问题

  很多问题找到了真正的原因,解决起来往往并不难,这个问题的原因是执行计划重用造成的,那么我们只需要解决执行计划重用的问题即可
  也就是不让他重用执行计划,只需要在SQL语句中加一个提示即可,
  也即:select COUNT(1) from Test20160810 where CreateDate>‘2016-6-1‘ and CreateDate<‘2016-6-9‘ OPTION(RECOMPILE)
  原因就在于加上OPTION(RECOMPILE)这个查询提示之后,不缓存SQL的执行计划缓存,没有了执行计划缓存,也就没得重用了

  比如这个查询,在查询语句中加入OPTION(RECOMPILE)查询提示,让其执行之前重编译SQL语句,他就可以正确地预估数据行了。

总结

本文通过一个实际案例说明了什么是简单参数模式下的自动化参数,自动化参数会带来哪些问题,以及如何解决,
问题本身非常简单,如果不注意还是会偶尔还是会出现困惑的。

题外话

有一点感受非常深,就是说,越来越多的实际问题,都要有理论知识作支撑,
但往往理论上说的情况并没有频繁出现或者即使出现了也没有引起注意,有时间就忽略了一些理论上的知识。
对于遇到的问题,如果真的要想弄清楚,还是要有一些理论知识做铺垫的。很多时候,往往是遇到问题之后,回忆起来曾经好似乎看过这一方面的理论知识。
这也是我们需要坚持看书,了解一些理论知识的原因。

时间: 2024-10-22 18:16:07

SQL Server SQL性能优化之--数据库在“简单”参数化模式下,自动参数化SQL带来的问题的相关文章

浅谈SQL Server中的事务日志(三)----在简单恢复模式下日志的角色

浅谈SQL Server中的事务日志(三)----在简单恢复模式下日志的角色 本篇文章是系列文章中的第三篇,前两篇的地址如下: 浅谈SQL Server中的事务日志(一)----事务日志的物理和逻辑构架 浅谈SQL Server中的事务日志(二)----事务日志在修改数据时的角色 简介 在简单恢复模式下,日志文件的作用仅仅是保证了SQL Server事务的ACID属性.并不承担具体的恢复数据的角色.正如”简单”这个词的字面意思一样,数据的备份和恢复仅仅是依赖于手动备份和恢复.在开始文章之前,首先

SQL Server 查询性能优化 相关文章

来自: SQL Server 查询性能优化——堆表.碎片与索引(一) SQL Server 查询性能优化——堆表.碎片与索引(二) SQL Server 查询性能优化——覆盖索引(一) SQL Server 查询性能优化——覆盖索引(二) SQL Server 查询性能优化——创建索引原则(一) SQL Server 查询性能优化——创建索引原则(二) SQL Server 查询性能优化——索引与SARG(一) SQL Server 查询性能优化——索引与SARG(二) SQL Server 查

浅谈SQL Server中的事务日志(四)----在完整恢复模式下日志的角色

浅谈SQL Server中的事务日志(四)----在完整恢复模式下日志的角色 本篇文章是系列文章中的第四篇,也是最后一篇,本篇文章需要前三篇的文章知识作为基础,前三篇的文章地址如下: 浅谈SQL Server中的事务日志(一)----事务日志的物理和逻辑构架 浅谈SQL Server中的事务日志(二)----事务日志在修改数据时的角色 浅谈SQL Server中的事务日志(三)----在简单恢复模式下日志的角色 简介 生产环境下的数据是如果可以写在资产负债表上的话,我想这个资产所占的数额一定不会

SET STATISTICS IO和SET STATISTICS TIME 在SQL Server查询性能优化中的作用

原文:SET STATISTICS IO和SET STATISTICS TIME 在SQL Server查询性能优化中的作用 近段时间以来,一直在探究SQL Server查询性能的问题,当然也漫无目的的查找了很多资料,也从网上的大神们的文章中学到了很多,在这里,向各位大神致敬.正是受大神们无私奉献精神的影响,所以小弟也作为回报,分享一下关于SET STATISTICS IO和SET STATISTICS TIME这两条T_SQL命令,在查询优化性能中的作用. 首先我想说明一下这篇文章不是关于如何

Sql Server查询性能优化之走出索引的误区

据了解绝大多数开发人员对于索引的理解都是一知半解,局限于大多数日常工作没有机会.也什么没有必要去关心.了解索引,实在哪天某个查询太慢了找到查询条件建个索引就ok,哪天又有个查询慢了,再建立个索引就是,或者干脆把整个查询SQL直接发给DBA,让DBA直接帮忙优化了,所以造成的状况就是开发人员对于索引的理解.认识很局限,以下就把我个人对于索引的理解及浅薄认识和大家分享下,希望能解除一些大家的疑惑,一起走出索引的误区 误区1.在表上建立了索引,在查询时用到了索引的列,索引就一定会生效 首先明确下这样的

SQL Server 查询性能优化——创建索引原则(一)

索引是什么?索引是提高查询性能的一个重要工具,索引就是把查询语句所需要的少量数据添加到索引分页中,这样访问数据时只要访问少数索引的分页就可以.但是索引对于提高查询性能也不是万能的,也不是建立越多的索引就越好.索引建少了,用WHERE子句找数据效率低,不利于查找数据.索引建多了,不利于新增.修改和删除等操作,因为做这些操作时,SQL SERVER除了要更新数据表本身,还要连带地立即更新所有的相关索引,而且过多的索引也会浪费硬盘空间.因此要建得恰到好处,这就需要经验了. 一:索引的基本目的 索引的基

SQL Server 查询性能优化——创建索引原则(二)

三:索引的建立原则 一般来说,建立索引要看数据使用的场景,换句话来说哪些访问数据的SQL语句是常用的,而这些语句是否因为缺少索引(也有可能是索引过多)变的效率低下.但绝不是所有的SQL语句都要建立索引,如果所有的SQL语句都建立索引,那么可能导致建立过多的索引. 我碰到过每秒钟新增记录超过千条的案例,虽然该数据表仅有聚集索引,但因为已存在的键值字段的值和新增数据键值字段的值并不是按顺序递增,每次新增记录时,肯定造成整体数据行的重新排列.在移掉聚集索引后,性能约提升20%.也曾经碰到过一个数据表上

最有效地优化 Microsoft SQL Server 的性能

为了最有效地优化 Microsoft SQL Server 的性能,您必须明确当情况不断变化时,性能将在哪些方面得到最大程度的改进,并集中分析这些方面.否则,在这些问题上您可能花费大量的时间和精力,而性能却得不到明显的改善. 以下大部分信息并不解决由多用户并发使用而引起的性能问题."Maximizing Database Consistency and Concurrency"(数据库一致性和并发性的最大化)一文以及其他知识库文章将对这个复杂的主题做单独的讨论,前者可从 SQL Ser

利用SET STATISTICS IO和SET STATISTICS TIME 优化SQL Server查询性能

首先需要说明的是这篇文章的内容并不是如何调节SQL Server查询性能的(有关这方面的内容能写一本书),而是如何在SQL Server查询性能的调节中利用SET STATISTICS IO和SET STATISTICS TIME这二条被经常忽略的Transact-SQL命令的. 从表面上看,查询性能的调节是一件十分简单的事.从本质上讲,我们希望查询的运行速度能够尽可能地快,无论是将查询运行的时间从10分钟缩减为1分钟,还是将运行的时间从2秒钟缩短为1秒种,我们最终的目标都是减少运行的时间. 尽