VARCHAR类型造成SQL SERVER经常死锁

[ 2010-07-29 14:18:21 | 作者: admin ]
字号: | |
SQL SERVER死锁的问题断断续续追踪了两三个星期,终于有个初步的判断:有一个SELECT语句和一个UPDATE语句需要获取大量的锁。死锁应该与之相关。
应用程序在SQL中为每个传入的字符串参数加了N,表示是unicode字符串。在参数与相应的列进行匹配的时候,如果该列不是NVARCHAR而是VARCHAR类型,则SQL SERVER要对该列的数据进行转换,由于此转换而导致不能使用索引,会获取大量的键锁、页锁。如果几个这样的SQL同时执行则容易死锁。
比如这样几句SQL:
declare @P1 int
set @P1=14
exec sp_prepexec @P1 output, N'@P1 nvarchar(4000)', N'SELECT CMDOCUMENTS.*, CMDOCTYPES.DOCTYPENAME, CMDOCCONTENTS.CONTENTSIZE, CMDOCCONTENTS.MIMETYPE FROM CMDOCUMENTS INNER JOIN CMDOCTYPES ON CMDOCUMENTS.DOCTYPEID = CMDOCTYPES.DOCTYPEID LEFT OUTER JOIN CMDOCCONTENTS ON CMDOCUMENTS.DOCID = CMDOCCONTENTS.DOCID WHERE CMDOCUMENTS.DOCID = @P1 ORDER BY CMDOCUMENTS.DOCNAME ASC', N'c373e90a87fa3a628f6acd567805f1f0'
select @P1
这是用事件探查器跟踪到的。
其中只有SELECT....是在java应用中写的SQL,前面的declare、set、exec以及后面的select @P1都应该是jdbc生成的。
declare @P1 int声明了一个int变量P1
set @P!=NULL 把P1赋值为空
exec sp_prepexec ....是执行SQL SERVER的存储过程sp_prepexec。@P1是一个输出参数,在存储过程执行后,P1会得到SQL SERVER分配的句柄。之后就可以用exec sp_execute引用这个句柄来再次执行此SQL。比如:
exec sp_execute 450, N'c373e90a87fa3a5f8f6acd567805f1f0', 2
这就是执行一个前面已分配句柄的SQL,句柄是450,后面是SQL中需要使用的参数。
好,现在说N。前面的几个SQL中可以看到在字符串参数值的前面有一个大写的N,它的含义是表明后面引号里的字符串是unicode,如果我没判断错的话,应该就是UTF-8。
为什么能判断出有了这个N之后就会获取大量的锁呢?是借助于查询分析器。
我把以下SQL贴到查询分析器中
SELECT * FROM CMDOCUMENTS WHERE CMDOCUMENTS.DOCID = N'c373e90a87fa3a7c8f6acd567805f1f0'
然后“显示估计的查询计划”
在CMDOCUMENTS.PK_...中看到对全部的索引做了扫描(scan),还有“成本100%”,并且其中“参数:”一栏的内容引起了我的怀疑,内容如下:
OBJECT:([v23test83].[v23test83].[CMDOCUMENTS].[PK_CMDOCUMENTS]),WHERE(Convert([CMDOCUMENTS].[DOCID])=[@1])
可以看到WHERE后面有对列DOCID使用了个函数convert,一般的数据库,如果一旦对列使用了函数就无法使用建立在该列上的索引,除非建立的是函数索引。
看表结构,DOCID是VARCHAR类型的。
把N去掉,再做分析
SELECT * FROM CMDOCUMENTS WHERE CMDOCUMENTS.DOCID = 'c373e90a87fa3a7c8f6acd567805f1f0'
仍然是“成本100%”,但是convert不见了。
使用事件探查器跟踪。有N的情况下,此查询会获取大量的锁。没有N的情况下,只获取非常少量的锁。
如果不去掉N,但把DOCID改为NVARCHAR类型,查询也不会获取大量的锁。
所以在SQL SERVER中使用N可要慎重。
[最后修改由 admin, 于 2010-07-29 14:21:16]
评论Feed 评论Feed: http://blog.xg98.com/feed.asp?q=comment&id=1436

这篇日志没有评论。

此日志不可发表评论。