今天我想进一步谈下SQL Server里的计划缓存和它的反作用。咱们都知道,每一个提交到SQL Server的逻辑查询,会编译为物理执行计划。这个执行计划而后会缓存为所谓的计划缓存,用于后期重用。如今咱们首先来谈下即席SQL语句和它的负做用,还有它们带来的性能问题。html
每次当你提交一个即席SQL语句到SQL Server,对于每一个特定查询,都会生成一个执行计划。“特定查询”是什么意思?答案很简单:SQL Server对每一个完整的SQL语句(包括你的参数值)生成一个哈希值,并使用这个哈希值做为计划缓存的查找值。若是使用这个哈希值找到一个执行计划,计划就会重用,不然在计划缓存里会编译一个新的执行计划。假设你提交下列3个查询到SQL Server:sql
SELECT * FROM Sales.SalesOrderHeader WHERE CustomerID = 11000 GO SELECT * FROM Sales.SalesOrderHeader WHERE CustomerID = 30052 GO SELECT * FROM Sales.SalesOrderHeader WHERE CustomerID = 11223 GO
对于这3个查询,SQL Server编译3个不一样的执行计划,由于你提供了硬编码的参数值。所以3个查询间会计算不一样的的哈希值,不会找到已缓存的计划。做为反作用,在计划缓存里,如今你有近3个近乎同样的查询有3个不一样的计划。这个特定问题被称为计划缓存污染。缓存
你刚用不一样的执行计划污染了你的计划缓存,这很难重用(由于硬编码的参数值),并且你在浪费能够被SQL Server里其它组件使用的有用内存。缓存的目的应该提升重用数,但使用即席SQL语句就作不到。性能
假设你为你的SQL语句使用参数值,或者你甚至使用存储过程。在这个状况下,SQL Server很是容易重用缓存的执行计划。但即便重用缓存的执行计划,你回引入性能问题。例如SQL Server为一个查询编译了一个执行计划,它回进行书签查找,由于非汇集索引没有覆盖你的查询:优化
咱们提过,书签查找只有在从表里获取一些数据才有意义。若是你越过了临界点,使用全表扫描或汇集索引扫描更高效。但若是SQL Server冲了缓存的执行计划,这个选项就不会考虑太多——SQL Server会盲目重用你的计划——这时你的性能就会不好!能够看下面的例子:编码
这里SQL盲目重用了有书签查找的缓存计划。你会看到估计和实际行数有很大差异!SQL Server在假设从查询里只返回一条记录来编译和缓存计划。但实际上从SQL Server咱们拿回1499条记录。你看到的执行计划,是假设只有一条记录返回的状况下优化——考虑下这个状况。spa
这里潜在的根源是你的计划不稳定。基于估计行数,你获得有书签查找的缓存计划,若是你越过临界点,会是表/汇集索引扫描。这是咱们常常碰到的常见SQL Server性能问题。code
你如何解决这个问题?简单:经过覆盖非汇集索引来避免书签查找。使用这个方法你会得到计划稳定性,无论你的输入参数,你会获得一样的性能。htm
今天你学到了SQL Server里,计划缓存的双刃剑:在一方面,计划缓存很是强大,由于你能够重用计划缓存避免编译资源占用。在另外一方面,他很是危险,使用定型的执行计划,你的计划再也不稳定,这就意味着你不能再保证性能。blog
感谢关注!