是否会成为问题—Linq to Sql的执行可能无法复…
2008-04-02 11:01:03来源:互联网 阅读 ()
查询计划
Sql Server在执行一条查询语句之前都对对他进行“编译”并生成“查询计划”,查询计划告诉Sql Server的查询引擎应该用什么方式进行工作。Sql Server会根据当前他能够收集到的各种信息(例如内存大小,索引的统计等等)把一条查询语句编译成他认为“最优”的查询计划。很显然,得到这样一个查询计划需要消耗CPU资源,而大部分的查询语句每次经过编译所得到的查询计划往往是相同的,因此除非指定了RECOMPILE选项,Sql Server在执行查询语句时,会对查询计划进行缓存——也就是说,假如是相同的查询语句,Sql Server只会对他进行一次编译操作,然后在每次执行时对查询计划进行复用。查询计划假如无法复用,则会在相当程度上降低数据库性能——因为过多的CPU被消耗在查询语句的编译上。各种提及数据库查询优化的资料上大都会提到这一点,我们往往通过查看性能计数器的某些统计,或Sql Server系统表中的一些记录,就能够判定您的数据库应用是否出现了这个问题。
对于存储过程来说,复用查询计划是轻而易举的。但是对于那些喜欢在程式代码中拼接Sql字符串的朋友来说,日子就有些不好过了。Sql Server是根据您传入的Sql语句来缓存查询计划的,假如您“强行”拼接了Sql字符串并交给Sql Server执行,那么查询计划被复用的可能性微乎其微。因此,我们绝对应该杜绝拼接字符串的行为,因为这不但仅造成了传统的Sql注入!而那些习惯相对较好的朋友,则会使用带参数的Sql语句,在交给Sql Server执行时就可能复用查询计划。因为和调用存储过程相比,发送带参数的Sql语句只是将使用了sp_executesql命令而已,每次执行的查询语句还是相同的。
问题何在?
对于复用查询计划的问题,在上文中我说了这么一句话:“……使用带参数的Sql语句,在交给Sql Server执行时就可能复用查询计划……”。我为什么要说“可能”?因为实时使用带参数的Sql语句,在某些情况下我们还是无法对查询计划进行复用。这是怎么一回事儿呢?我们还是直接从Linq to Sql来产生Sql语句,然后观察Sql Server的行为吧。
请看以下的代码(示例所操作的数据表和《在Linq to Sql中管理并发更新时的冲突(2):引发更新冲突》一文相同):
LinqToSqlDemoDataContext dataContext = new LinqToSqlDemoDataContext(); dataContext.Log = Console.Out; Video video1 = dataContext.Videos.SingleOrDefault( v => v.Introduction == "Hello"); Video video2 = dataContext.Videos.SingleOrDefault( v => v.Introduction == "Hello World"); Console.ReadLine(); |
还是查看输出:
SELECT [t0].[VideoID], [t0].[Introduction], [t0].[SiteID] FROM [dbo].[Video] AS [t0] WHERE [t0].[Introduction] = @p0 -- @p0: Input NVarChar (Size = 5; Prec = 0; Scale = 0) [Hello] -- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 3.5.21004.1 SELECT [t0].[VideoID], [t0].[Introduction], [t0].[SiteID] FROM [dbo].[Video] AS [t0] WHERE [t0].[Introduction] = @p0 -- @p0: Input NVarChar (Size = 11; Prec = 0; Scale = 0) [Hello World] -- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 3.5.21004.1 |
两局Sql语句完全相同,按我们刚才的说法,Sql Server应该缓存了查询计划。但是我们通过查看sys.syscacheobjects的相关数据能够看出,事情并非如同我们想象的那样:
SELECT cacheobjtype, sql FROM sys.syscacheobjects; DBCC freeproccache; |
请注意上图中被选中的两条记录,他表明了Sql Server并没有缓存执行计划。
为什么?这两次执行究竟有什么区别?通过Linq to Sql很容易看出,两次执行所用到的参数不同。更进一步,假如对比Linq to Sql输出的缓存连同sys.syscacheobjects视图中的记录,就会发现:其实仅仅是参数的尺寸不同。
没错,就是这个原因。在使用ADO.NET时,假如SqlParameter的Type是nvarchar,并且没有指定Size属性,则可能就会因为具体参数的尺寸不同而造成查询计划无法复用的结果。这一点,很多人都忽视了。
优化方案
在使用ADO.NET进行研发时,该问题其实很容易解决。我们只要指定SqlParameter的Size属性即可。由于每次指定了一个固定的参数尺寸,Sql Server就能够复用查询计划了。
但是我们现在在使用Linq to Sql,又该怎么做呢?嗯,我们能够为XXXXDataContext重写(override)SubmitChanges方法,在其中获得需要执行的SqlCommand对象(具体方法请参考《在Linq to Sql中管理并发更新时的冲突(1):预备知识》一文),获得其中的SqlParameter参数,并设定他们的Size属性。我们能够使用Custom Attribute来标注应该为哪个属性配置什么样的Size,假如再结合AOP,哈哈……
标签:
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有
IDC资讯: 主机资讯 注册资讯 托管资讯 vps资讯 网站建设
网站运营: 建站经验 策划盈利 搜索优化 网站推广 免费资源
网络编程: Asp.Net编程 Asp编程 Php编程 Xml编程 Access Mssql Mysql 其它
服务器技术: Web服务器 Ftp服务器 Mail服务器 Dns服务器 安全防护
软件技巧: 其它软件 Word Excel Powerpoint Ghost Vista QQ空间 QQ FlashGet 迅雷
网页制作: FrontPages Dreamweaver Javascript css photoshop fireworks Flash