达达's 胡搞瞎搞工作室

此Blog搬到www.unbe.cn/blog 去了,要找我讨论问题请到我的新Blog

关于SQL 参数的一次测试(重新又测了一次)

    园里的henry前些天提醒我在封装SQL语句的时候需要注意参数名尽量重复使用,不要每次都用不一样的参数名,他说如果每次参数名都用不一样SQL Server内存会越占越大,他曾经试过占到1G内存。我没有做过这方面的试验,光听henry讲心里没什么底,询问了厦门.NET具乐部里的朋友们,他们都以为我是在说SqlParameter没有被CG回收。。。一个劲的说Clear,Dispose,using。。。这也不能怪他们,是我表达的不清楚。最后没办法,我自己做了个测试。

    测试所用的表结构很简单:
CREATE TABLE [dbo].[MyTable] (
    
[Code] [char] (50) COLLATE Chinese_PRC_CI_AS NULL
 ,
    
[CreateTime] [datetime] NULL
 
ON [PRIMARY]

GO

    测试分两次,一次向数据库插入10W条记录,在同一次数据库连接和事物中完成,第一次测试每次插入用的SQL参数是用Guid生成的不重复的参数名,第二次测试每次插入用的参数名都是一样的。

    测试结果如下:

   

   从测试结果可以看出两次测试的结果差别巨大,使用不重复的参数名不光吃了很多的内存还执行缓慢!

   小弟不材,测试会做,但是不知道为什么SQL Server会有这样的问题,希望路过的大虾可以帮小弟解答下为什么会有这样的情况。

   我测试用的代码(请各位用自己的数据层或者单纯ADO.NET替换我的数据层部分)
   这是第二次测试用的代码,和第一次测试唯一区别就是用固定的参数名代替了原来的Guid。  
DateTime _t1 = DateTime.Now;
using (DatabaseSession _seesion = new DatabaseSession("TestDB"
))
{
    
for (int i = 0; i < 100000; i++
)
    {
        
string _par1 = "par1"//Guid.NewGuid().ToString().Replace("-", "");

        string _par2 = "par2"//Guid.NewGuid().ToString().Replace("-", "");
        string _sql = string.Format("Insert into MyTable (Code,CreateTime) values(@{0},@{1})", _par1, _par2);

        IDataParameter _param1 
= _da.CreateDataParameter(string.Format("@{0}"
, _par1), 
            DbType.String, Guid.NewGuid().ToString());
        IDataParameter _param2 
= _da.CreateDataParameter(string.Format("@{0}"
, _par2), 
            DbType.DateTime, DateTime.Now);

        _da.ExecuteNonQuery(_sql, _seesion.DbConnection, _seesion.DbTransaction, _param1, _param2);
    }
}

    介于大家对字符串操作影响代码效率的问题,我重新设计了一次测试,这次测试的结果是:

    使用重复的参数名,插入100000条记录,使用了40多秒的时间,刚开始运行时SQL Server内存占用12M,运行时最高峰占29M,结束时占内存26M。
    使用不重复的参数名,插入100000条记录,使用了190多秒的时间,刚开始运行时SQL Server内存占用26M,运行时最高峰为305M,结束时占内存96M。

    测试代码如下:
DateTime _t1 = DateTime.Now;

List
<ExecuteBatchItem> _bath = new List<ExecuteBatchItem>
();

//预备批查询的所有项,此时还未连接数据库和执行数据库操作

for (int i = 0; i < 100000; i++)
{
    
string _par1 = Guid.NewGuid().ToString().Replace("-""");//"par1"; 

    string _par2 = Guid.NewGuid().ToString().Replace("-""");//"par2"; 
    string _sql = string.Format("Insert into MyTable (Code,CreateTime) values(@{0},@{1})", _par1, _par2);

    List
<IDataParameter> _params = new List<IDataParameter>
();

    IDataParameter _param1 
= _da.CreateDataParameter(string.Format("@{0}"
, _par1),
        DbType.String, Guid.NewGuid().ToString());
    IDataParameter _param2 
= _da.CreateDataParameter(string.Format("@{0}"
, _par2),
        DbType.DateTime, DateTime.Now);

    _params.Add(_param1);
    _params.Add(_param2);

    _bath.Add(
new
 ExecuteBatchItem(_sql, _params.ToArray(), ExecuteTypes.ExecuteNonQuery));
}

//我的数据层的批查询方法,在同一次数据库连接中执行_bath里的所有查询操作

DataAccess.Create("TestDB").ExecuteBatch(_bath, true);

//计算时间

Response.Write(((TimeSpan)(DateTime.Now - _t1)).TotalMilliseconds);

第二次的测试可以看出字符串操作并不是影响SQL Server执行的关键,也更不可能会影响SQL Server的内存占用,应为字符串是.NET程序域内存的一部分而不是SQL Server的

posted on 2006-07-17 14:06 达达 阅读(2231) 评论(13)  编辑 收藏 网摘

Feedback

#1楼  2006-07-17 14:49 none [未注册用户]

string _par1 = "par1"; //Guid.NewGuid().ToString().Replace("-", "");
string _par2 = "par2"; //Guid.NewGuid().ToString().Replace("-", "");
我想第一次慢的原因不是SQL Server吧?
而是Guid.NewGuid().ToString().Replace造的吧?
  回复  引用    

#2楼  2006-07-17 14:51 Anders06      

SQL不太懂,
这个应该跟缓存有关,以前似乎看到,同一条sql语句当你的参数名不同时,是不会共享一个已有缓存里建立的对象(忘记这个对象是什么了,DbConnection?)
  回复  引用  查看    

#3楼 [楼主] 2006-07-17 14:52 达达      

@none
呵呵,也许有点
  回复  引用  查看    

#4楼 [楼主] 2006-07-17 14:53 达达      

@none
但是如果这样的话SQL server内存不会占用这么大,而且速度也不应该差别这么大
  回复  引用  查看    

#5楼  2006-07-17 15:04 Goodspeed      

参数化查询将有利于sql server缓存编译的结果.这是速度加快的主要原因.

在sql server 2005中,你可以强制打开参数化查询
  回复  引用  查看    

#6楼 [楼主] 2006-07-17 15:07 达达      

@Goodspeed
那第一次测试不也是参数化的吗,怎么内存都被光了。。。。速度也超慢
  回复  引用  查看    

#7楼  2006-07-17 15:51 东海风      

我估计SqlServer会缓存Sql语句的编译结果,如果Sql语句没有变化而且参数名固定,SqlServer可以直接使用编译好的结果,而如果参数名变了,SqlServer会认为是一条新语句而重新编译。如果每次执行时使用不同的Sql语句,或许不会出现这种现象。问题是怎么样去做这种测试呢?总不能随机生成Sql语句吧,再说了,不同的Sql语句执行速度会不一样,将使测试失去可比性。   回复  引用  查看    

#8楼  2006-07-17 16:58 Ivan      

我在书上也看到过有关使用重复参数的意义. 强烈建议使用相同名字参数.   回复  引用  查看    

#9楼  2006-07-17 17:48 code [未注册用户]

我想C#也有影响你所说的性能问题,因为在C#中定义字符串变量后,以后对变量的操作不是直接引用变量,而是直接复制变量,这样插入2000条记录,两个参数就是复制了40000次   回复  引用    

#10楼  2006-07-17 21:35 丁丁      

参数名用Guid.NewGuid().ToString().Replace("-", ""),楼主还是挺幽默的,东海风说的没错,其实从现象上也已经可以推定结论了   回复  引用  查看    

#11楼  2006-07-18 08:03       

1、个人的直觉是Guid好像都不用放在客户端的代码里,一般设为字段的黙认值
,所以大胆认为这样的比较的前提就不佳
2、执行时间的概念:去除事务的概念不说,至少应分为客户端代码的执行时间和T-SQL在服务器的执行时间。两者也应该很好的区分的

以上供参考,建议大家都去研究SQL2005的例子和报表服务数据库,当然还有内置数据库,特别是系统存储过程。

像组合式查询单词网站:http://www.pinstudy.com的查询时间就是仅计算T-SQL的结果
  回复  引用  查看    

#12楼  2006-07-18 09:11 henry      

为什么不同参数名会影响MSSQL的内存问题才是重点!
对于两段测试代码的性能差别是次要的,可能楼主在写代码没有注意string的特殊性.
看来楼主这回是找不到答案的了:)

  回复  引用  查看    

#13楼  2006-07-25 16:22 大坏蛋[] [未注册用户]

这个问题是SQL Server特性引发的,为什么我们要参数化而不是拼SQL,其中一个原因就是,参数化查询会被预编译并将编译结果在内存中缓冲,下次调用会重用现有编译结果,每次参数名变化,SQL Server认为是不同的参数化查询,故每次查询都在内存中生成一个编译缓冲(1),且没有得到复用(2) 1是内存增长的主要原因,二是慢的原因,Guid.NewGuid()也是内存增长的次因。所以不要怪SQL Server和.NET。SQL Server完全可以做到仅参数名不同时也能重用编译结果,但带来的是性能的损失。   回复  引用    


发表评论



姓名 [登录] [注册] 
主页
Email (仅博主可见) 
验证码 *  验证码看不清,换一张
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论   新用户注册   返回页首      

导航: 网站首页 社区 新闻 博问 闪存 网摘 招聘 .NET频道 知识库 找找看 Google站内搜索



China-pub 计算机图书网上专卖店!6.5万品种 2-8折!
China-Pub 计算机绝版图书按需印刷服务

相关文章:

相关链接:
 

导航

统计信息

News

与我联系

搜索

 

常用链接

随笔档案(104)

文章分类(10)

积分与排名