如何用参数化SQL语句污染你的计划缓存

简介:

你的SQL语句的参数化总是个好想法。使用参数化SQL语句你不会污染你的计划缓存——错!!!在这篇文章里我想向你展示下用参数化SQL语句就可以污染你的计划缓存,这是非常简单的!

ADO.NET-AddWithValue

ADO.NET是实现像SQL Server关系数据库数据访问的.NET框架的组成——有一些严重的副作用。不要误解我——只要你正确使用,ADO.NET一直很棒。你马上就会看到,它很容易被错误使用。我们来看下面实现SQL语句执行的C#代码。 

复制代码
 1 for (int i = 1; i <= 100; i++)
 2 {
 3    val += i.ToString();
 4 
 5    cmd = new SqlCommand(
 6       "SELECT * FROM Sales.SalesOrderDetail WHERE CarrierTrackingNumber = @CarrierTrackingNumber", 
 7       cnn);
 8    cmd.Parameters.AddWithValue("@CarrierTrackingNumber", val);
 9    SqlDataReader reader = cmd.ExecuteReader();
10    reader.Close();
11 }
复制代码

我们是聪明的开发者,因此SQL语句本身被参数化,因为ADO.NET框架是地球上最棒的框架,我们使用System.Data.SqlClient.SqlParameterCollection类的AddWithValue方法来提供实际的参数值。我在WHLIE循环里运行那个SQL语句100次,总用不同长度赋予参数值。在Sales.SalesOrderDetail表里CarrierTrackingNumber列定义为NVARCHAR(25)。因此我们可以在基于我们提供的不同字符长度上有上至25个不同数据类型的参数。现在让我们检查下我们SQL语句执行后的计划缓存。

复制代码
1 SELECT
2     st.text,
3     cp.*
4 FROM sys.dm_exec_cached_plans cp
5 CROSS APPLY sys.dm_exec_sql_text(cp.plan_handle) st
6 GO
复制代码

现在事情变得有点疯狂:在计划缓存里我们存储了100个不同的执行计划!

 

对于每个可能的数据类型参数都有1个执行计划——即使当数据类型是NVACHAR(25)AddWithValue方法非常,非常邪恶:基于你提供的参数值派生出数据类型。永远不要使用它!

ADO.NET – SqlDbType.VarChar

因为从我们的错误中我们学到了,现在我们知道ADO.NET的AddWithValue方法的副作用——我们不再用它。现在让我们重写我们的C#程序代码,如下所示定义一个显示的参数数据类型: 

复制代码
 1 for (int i = 1; i <= 100; i++)
 2 {
 3    val += i.ToString();
 4 
 5    cmd = new SqlCommand(
 6       "SELECT * FROM Sales.SalesOrderDetail WHERE CarrierTrackingNumber = @CarrierTrackingNumber",
 7       cnn);
 8    cmd.Parameters.Add(new SqlParameter("@CarrierTrackingNumber", SqlDbType.VarChar));
 9    cmd.Parameters["@CarrierTrackingNumber"].Value = val;
10    SqlDataReader reader = cmd.ExecuteReader();
11    reader.Close();
12 }
复制代码

从代码里你可以看到,ADO.NET现在不能派生参数数据类型了,因为我们已经指定了SqlDbType.Varchar数据类型。让我们再次执行这个SQL语句100次并再次检查下计划缓存:

 

没有啥改变。问题还是一样:在计划缓存里我们还有100个不一样的的执行计划。现在的问题是ADO.NET只强制数据类型(SqlDbType.VarChar),但不是数据类型的"长度"。有100个不同的长度在计划缓存里你就有100个不同的执行计划。

如果你在你的ADO.NET代码里显式指定参数数据类型,你也要指定它的长度!现在我们来看下一些修正的C#代码。

复制代码
 1 for (int i = 1; i <= 100; i++)
 2 {
 3    val += i.ToString();
 4 
 5    cmd = new SqlCommand(
 6       "SELECT * FROM Sales.SalesOrderDetail WHERE CarrierTrackingNumber = @CarrierTrackingNumber",
 7       cnn);
 8    cmd.Parameters.Add(new SqlParameter("@CarrierTrackingNumber", SqlDbType.VarChar, 100));
 9    cmd.Parameters["@CarrierTrackingNumber"].Value = val;
10    SqlDataReader reader = cmd.ExecuteReader();
11    reader.Close();
12 }
复制代码

这次我也指定了数据类型的长度——这里是100,现在当我们再次执行SQL语句100次时,最后我们在计划缓存里以1个执行计划且重用了100次来完美收工。这是从SQL Server角度的最终目标。

 

小结

寓意:ADO.NET是个很棒的数据访问框架,它提供你有用的功能(例如AddWithValue方法),当从SQL Server角度来说你真的要考虑下你在做什么。当你使用参数化SQL语句时,你要尽量显式:你必须地冠以参数值的实际数据类型,还有你想要的获得数据类型长度。



本文转自Woodytu博客园博客,原文链接:http://www.cnblogs.com/woodytu/p/4751915.html,如需转载请自行联系原作者

相关文章
|
2月前
|
SQL 缓存 关系型数据库
MySQL技能完整学习列表6、查询优化——3、查询缓存——4、SQL优化技巧
MySQL技能完整学习列表6、查询优化——3、查询缓存——4、SQL优化技巧
65 0
|
8月前
|
SQL 缓存 自然语言处理
SQL参数化查询为什么能够防止SQL注入
SQL参数化查询为什么能够防止SQL注入
101 0
|
5月前
|
SQL 存储 缓存
Flink CDC中flink sql 如果缓存起来所有的数据,然后基于这个数据做查询?
Flink CDC中flink sql 如果缓存起来所有的数据,然后基于这个数据做查询?
63 1
|
7月前
|
SQL 缓存 OLTP
OBCP第三章 SQL引擎技术-执行计划缓存
OBCP第三章 SQL引擎技术-执行计划缓存
90 0
|
8月前
|
SQL 缓存 Java
mybatis执行sql流程和缓存超级详解
mybatis执行sql流程和缓存超级详解
72 0
|
9月前
|
存储 SQL 缓存
MySQL高级第三篇(共四篇)之应用优化、查询缓存优化、内存管理优化、MySQL锁问题、常用SQL技巧(二)
锁是计算机协调多个进程或线程并发访问某一资源的机制(避免争抢)。 在数据库中,除传统的计算资源(如 CPU、RAM、I/O 等)的争用以外,数据也是一种供许多用户共享的资源。如何保证数据并发访问的一致性、有效性是所有数据库必须解决的一个问题,锁冲突也是影响数据库并发访问性能的一个重要因素。从这个角度来说,锁对数据库而言显得尤其重要,也更加复杂。
369 0
|
9月前
|
SQL 存储 缓存
MySQL高级第三篇(共四篇)之应用优化、查询缓存优化、内存管理优化、MySQL锁问题、常用SQL技巧(一)
前面章节,我们介绍了很多数据库的优化措施。但是在实际生产环境中,由于数据库本身的性能局限,就必须要对前台的应用进行一些优化,来降低数据库的访问压力。
15969 7
|
9月前
|
SQL 缓存 Java
【MyBatis】day03动态SQL和缓存机制
【MyBatis】day03动态SQL和缓存机制
66 0
|
10月前
|
SQL XML 缓存
Mybatis 动态sql的编写|开启二级缓存(下)
Mybatis 动态sql的编写|开启二级缓存
93 0
|
10月前
|
SQL XML 缓存
Mybatis 动态sql的编写|开启二级缓存(上)
Mybatis 动态sql的编写|开启二级缓存
76 0

热门文章

最新文章