本人用ASP+SQLserver2005做了一个网站,访问量不是很大,每天500IP左右,由于第一次用SQLserver,不太懂得优化,发现网站在运行一段时间后SQLserver大量占用内存,我服务器2G的内存,SQLserver能占到1.3G,请问什么原因会导致SQLserver大量占用内存不释放?怎样查找根源并做优化?谢谢大家了。

解决方案 »

  1.   

    何让你的SQL运行得更快(转贴)           
        ----       人们在使用SQL时往往会陷入一个误区,即太关注于所得的结果是否正确,而忽略       
        了不同的实现方法之间可能存在的性能差异,这种性能差异在大型的或是复杂的数据库       
        环境中(如联机事务处理OLTP或决策支持系统DSS)中表现得尤为明显。笔者在工作实践       
        中发现,不良的SQL往往来自于不恰当的索引设计、不充份的连接条件和不可优化的whe       
        re子句。在对它们进行适当的优化后,其运行速度有了明显地提高!下面我将从这三个       
        方面分别进行总结:       
        ----       为了更直观地说明问题,所有实例中的SQL运行时间均经过测试,不超过1秒的均       
        表示为( <       1秒)。       
        ----       测试环境--       
        ----       主机:HP       LH       II       
        ----       主频:330MHZ       
        ----       内存:128兆       
        ----       操作系统:Operserver5.0.4       
        ----数据库:Sybase11.0.3       
        一、不合理的索引设计       
        ----例:表record有620000行,试看在不同的索引下,下面几个       SQL的运行情况:       
        ----       1.在date上建有一非个群集索引       
        select       count(*)       from       record       where       date       >       
        '19991201'       and       date       <       '19991214'and       amount       >       
        2000       (25秒)       
        select       date,sum(amount)       from       record       group       by       date       
        (55秒)       
        select       count(*)       from       record       where       date       >       
        '19990901'       and       place       in       ('BJ','SH')       (27秒)       
        ----       分析:       
        ----date上有大量的重复值,在非群集索引下,数据在物理上随机存放在数据页上,在       
        范围查找时,必须执行一次表扫描才能找到这一范围内的全部行。       
        ----       2.在date上的一个群集索引       
        select       count(*)       from       record       where       date       >       
        '19991201'       and       date       <       '19991214'       and       amount       >       
        2000       (14秒)       
        select       date,sum(amount)       from       record       group       by       date       
        (28秒)       
        select       count(*)       from       record       where       date       >       
        '19990901'       and       place       in       ('BJ','SH')(14秒)       
        ----       分析:       
        ----       在群集索引下,数据在物理上按顺序在数据页上,重复值也排列在一起,因而在范       
        围查找时,可以先找到这个范围的起末点,且只在这个范围内扫描数据页,避免了大范       
        围扫描,提高了查询速度。       
        ----       3.在place,date,amount上的组合索引       
        select       count(*)       from       record       where       date       >       
        '19991201'       and       date       <       '19991214'       and       amount       >       
        2000       (26秒)       
        select       date,sum(amount)       from       record       group       by       date       
        (27秒)       
        select       count(*)       from       record       where       date       >       
        '19990901'       and       place       in       ('BJ,       'SH')( <       1秒)       
        ----       分析:       
        ----       这是一个不很合理的组合索引,因为它的前导列是place,第一和第二条SQL没有引       
        用place,因此也没有利用上索引;第三个SQL使用了place,且引用的所有列都包含在组       
        合索引中,形成了索引覆盖,所以它的速度是非常快的。       
        ----       4.在date,place,amount上的组合索引       
        select       count(*)       from       record       where       date       >       
        '19991201'       and       date       <       '19991214'       and       amount       >       
        2000( <       1秒)       
        select       date,sum(amount)       from       record       group       by       date       
        (11秒)       
        select       count(*)       from       record       where       date       >       
        '19990901'       and       place       in       ('BJ','SH')( <       1秒)       
        ----       分析:       
        ----       这是一个合理的组合索引。它将date作为前导列,使每个SQL都可以利用索引,并       
        且在第一和第三个SQL中形成了索引覆盖,因而性能达到了最优。       
        ----       5.总结:       
        ----       缺省情况下建立的索引是非群集索引,但有时它并不是最佳的;合理的索引设计要       
        建立在对各种查询的分析和预测上。一般来说:       
        ----       ①.有大量重复值、且经常有范围查询       
        (between,       > , <       ,> =, <       =)和order       by       
        、group       by发生的列,可考虑建立群集索引;       
        ----       ②.经常同时存取多列,且每列都含有重复值可考虑建立组合索引;       
        ----       ③.组合索引要尽量使关键查询形成索引覆盖,其前导列一定是使用最频繁的列。       
                        
      

  2.   

    何让你的SQL运行得更快(转贴)           
        ----       人们在使用SQL时往往会陷入一个误区,即太关注于所得的结果是否正确,而忽略       
        了不同的实现方法之间可能存在的性能差异,这种性能差异在大型的或是复杂的数据库       
        环境中(如联机事务处理OLTP或决策支持系统DSS)中表现得尤为明显。笔者在工作实践       
        中发现,不良的SQL往往来自于不恰当的索引设计、不充份的连接条件和不可优化的whe       
        re子句。在对它们进行适当的优化后,其运行速度有了明显地提高!下面我将从这三个       
        方面分别进行总结:       
        ----       为了更直观地说明问题,所有实例中的SQL运行时间均经过测试,不超过1秒的均       
        表示为( <       1秒)。       
        ----       测试环境--       
        ----       主机:HP       LH       II       
        ----       主频:330MHZ       
        ----       内存:128兆       
        ----       操作系统:Operserver5.0.4       
        ----数据库:Sybase11.0.3       
        一、不合理的索引设计       
        ----例:表record有620000行,试看在不同的索引下,下面几个       SQL的运行情况:       
        ----       1.在date上建有一非个群集索引       
        select       count(*)       from       record       where       date       >       
        '19991201'       and       date       <       '19991214'and       amount       >       
        2000       (25秒)       
        select       date,sum(amount)       from       record       group       by       date       
        (55秒)       
        select       count(*)       from       record       where       date       >       
        '19990901'       and       place       in       ('BJ','SH')       (27秒)       
        ----       分析:       
        ----date上有大量的重复值,在非群集索引下,数据在物理上随机存放在数据页上,在       
        范围查找时,必须执行一次表扫描才能找到这一范围内的全部行。       
        ----       2.在date上的一个群集索引       
        select       count(*)       from       record       where       date       >       
        '19991201'       and       date       <       '19991214'       and       amount       >       
        2000       (14秒)       
        select       date,sum(amount)       from       record       group       by       date       
        (28秒)       
        select       count(*)       from       record       where       date       >       
        '19990901'       and       place       in       ('BJ','SH')(14秒)       
        ----       分析:       
        ----       在群集索引下,数据在物理上按顺序在数据页上,重复值也排列在一起,因而在范       
        围查找时,可以先找到这个范围的起末点,且只在这个范围内扫描数据页,避免了大范       
        围扫描,提高了查询速度。       
        ----       3.在place,date,amount上的组合索引       
        select       count(*)       from       record       where       date       >       
        '19991201'       and       date       <       '19991214'       and       amount       >       
        2000       (26秒)       
        select       date,sum(amount)       from       record       group       by       date       
        (27秒)       
        select       count(*)       from       record       where       date       >       
        '19990901'       and       place       in       ('BJ,       'SH')( <       1秒)       
        ----       分析:       
        ----       这是一个不很合理的组合索引,因为它的前导列是place,第一和第二条SQL没有引       
        用place,因此也没有利用上索引;第三个SQL使用了place,且引用的所有列都包含在组       
        合索引中,形成了索引覆盖,所以它的速度是非常快的。       
        ----       4.在date,place,amount上的组合索引       
        select       count(*)       from       record       where       date       >       
        '19991201'       and       date       <       '19991214'       and       amount       >       
        2000( <       1秒)       
        select       date,sum(amount)       from       record       group       by       date       
        (11秒)       
        select       count(*)       from       record       where       date       >       
        '19990901'       and       place       in       ('BJ','SH')( <       1秒)       
        ----       分析:       
        ----       这是一个合理的组合索引。它将date作为前导列,使每个SQL都可以利用索引,并       
        且在第一和第三个SQL中形成了索引覆盖,因而性能达到了最优。       
        ----       5.总结:       
        ----       缺省情况下建立的索引是非群集索引,但有时它并不是最佳的;合理的索引设计要       
        建立在对各种查询的分析和预测上。一般来说:       
        ----       ①.有大量重复值、且经常有范围查询       
        (between,       > , <       ,> =, <       =)和order       by       
        、group       by发生的列,可考虑建立群集索引;       
        ----       ②.经常同时存取多列,且每列都含有重复值可考虑建立组合索引;       
        ----       ③.组合索引要尽量使关键查询形成索引覆盖,其前导列一定是使用最频繁的列。       
                        
      

  3.   

    首先,我们来看看MSSQL是怎样使用内存的。  最大的开销一般是用于数据缓存,如果内存足够,它会把用过的数据和觉得你会用到的数据统统扔到内存中,直到内存不足的时候,才把命中率低的数据给清掉。所以一般我们在看statistics io的时候,看到的physics read都是0。  其次就是查询的开销,一般地说,hash join是会带来比较大的内存开销的,而merge join和nested loop的开销比较小,还有排序和中间表、游标也是会有比较大的开销的。  所以用于关联和排序的列上一般需要有索引。  再其次就是对执行计划、系统数据的存储,这些都是比较小的。  我们先来看数据缓存对性能的影响,如果系统中没有其它应用程序来争夺内存,数据缓存一般是越多越好,甚至有些时候我们会强行把一些数据pin在高速缓存中。但是如果有其它应用程序,虽然在需要的时候MSSQL会释放内存,但是线程切换、IO等待这些工作也是需要时间的,所以就会造成性能的降低。这样我们就必须设置MSSQL的最大内存使用。可以在SQL Server 属性(内存选项卡)中找到配置最大使用内存的地方,或者也可以使用sp_configure来完成。如果没有其它应用程序,那么就不要限制MSSQL对内存的使用。  然后来看查询的开销,这个开销显然是越低越好,因为我们不能从中得到好处,相反,使用了越多的内存多半意味着查询速度的降低。所以我们一般要避免中间表和游标的使用,在经常作关联和排序的列上建立索引。 
      

  4.   

    --设置 min server memory 配置项
    EXEC sp_configure N'min server memory (MB)', 0--设置 max server memory 配置项
    EXEC sp_configure N'max server memory (MB)', 256--使更新生效
    RECONFIGURE WITH OVERRIDE
      

  5.   

    解决SQL Server占用内存过多的问题来源:IT专家网
      经常看见有人问,MSSQL占用了太多的内存,而且还不断的增长; 或者说已经设置了使用内存,可是它没有用到那么多,这是怎么一回事儿呢?  首先,我们来看看MSSQL是怎样使用内存的。  最大的开销一般是用于数据缓存,如果内存足够,它会把用过的数据和觉得你会用到的数据统统扔到内存中,直到内存不足的时候,才把命中率低的数据给清掉。所以一般我们在看statistics io的时候,看到的physics read都是0。  其次就是查询的开销,一般地说,hash join是会带来比较大的内存开销的,而merge join和nested loop的开销比较小,还有排序和中间表、游标也是会有比较大的开销的。  所以用于关联和排序的列上一般需要有索引。  再其次就是对执行计划、系统数据的存储,这些都是比较小的。  我们先来看数据缓存对性能的影响,如果系统中没有其它应用程序来争夺内存,数据缓存一般是越多越好,甚至有些时候我们会强行把一些数据pin在高速缓存中。但是如果有其它应用程序,虽然在需要的时候MSSQL会释放内存,但是线程切换、IO等待这些工作也是需要时间的,所以就会造成性能的降低。这样我们就必须设置MSSQL的最大内存使用。可以在SQL Server 属性(内存选项卡)中找到配置最大使用内存的地方,或者也可以使用sp_configure来完成。如果没有其它应用程序,那么就不要限制MSSQL对内存的使用。  然后来看查询的开销,这个开销显然是越低越好,因为我们不能从中得到好处,相反,使用了越多的内存多半意味着查询速度的降低。所以我们一般要避免中间表和游标的使用,在经常作关联和排序的列上建立索引。 
      

  6.   

    执行缓存以优化SQL Server的内存占用作者:superhasty  2007-11-29     在论坛上常见有朋友抱怨,说SQL Server太吃内存了。这里笔者根据经验简单介绍一下内存相关的调优知识。首先说明一下SQL Server内存占用由哪几部分组成。SQL Server占用的内存主要由三部分组成:数据缓存(Data Buffer)、执行缓存(Procedure Cache)、以及SQL Server引擎程序。SQL Server引擎程序所占用缓存一般相对变化不大,则我们进行内存调优的主要着眼点在数据缓存和执行缓存的控制上。本文主要介绍一下执行缓存的调优。数据缓存的调优将在另外的文章中介绍。     对于减少执行缓存的占用,主要可以通过使用参数化查询减少内存占用。     1、使用参数化查询减少执行缓存占用 
    我们通过如下例子来说明一下使用参数化查询对缓存占用的影响。为方便试验,我们使用了一台没有其它负载的SQL Server进行如下实验。     下面的脚本循环执行一个简单的查询,共执行10000次。     首先,我们清空一下SQL Server已经占用的缓存:     dbcc freeproccache     然后,执行脚本: DECLARE @t datetime
    SET @t = getdate()
    SET NOCOUNT ON
    DECLARE @i INT, @count INT, @sql nvarchar(4000)
    SET @i = 20000
    WHILE @i <= 30000
    BEGIN
    SET @sql = 'SELECT @count=count(*) FROM P_Order WHERE MobileNo = ' + cast( @i as varchar(10) )
    EXEC sp_executesql @sql ,N'@count INT OUTPUT', @count OUTPUT
    SET @i = @i + 1
    END
    PRINT DATEDIFF( second, @t, current_timestamp ) 
        输出: DBCC 执行完毕。如果 DBCC 输出了错误信息,请与系统管理员联系。     11 
        使用了11秒完成10000次查询。     我们看一下SQL Server缓存中所占用的查询计划: Select Count(*) CNT,sum(size_in_bytes) TotalSize From sys.dm_exec_cached_plans
        查询结果:共有2628条执行计划缓存在SQL Server中。它们所占用的缓存达到: 
        92172288字节 = 90012KB = 87 MB。     我们也可以使用dbcc memorystatus 命令来检查SQL Server的执行缓存和数据缓存占用。     执行结果如下:     执行缓存占用了90088KB,有2629个查询计划在缓存里,有1489页空闲内存(每页8KB)可以被数据缓存和其他请求所使用。     我们现在修改一下前面的脚本,然后重新执行一下dbcc freeproccache。再执行一遍修改后的脚本: DECLARE @t datetime
    SET @t = getdate()
    SET NOCOUNT ON
    DECLARE @i INT, @count INT, @sql nvarchar(4000)
    SET @i = 20000
    WHILE @i <= 30000
    BEGIN
    SET @sql = 'select @count=count(*) FROM P_Order WHERE MobileNo = @i'
    EXEC sp_executesql @sql, N'@count int output, @i int', @count OUTPUT, @i
    SET @i = @i + 1
    END
    PRINT DATEDIFF( second, @t, current_timestamp )
        输出:     DBCC 执行完毕。如果 DBCC 输出了错误信息,请与系统管理员联系。 
        1     即这次只用1秒钟即完成了10000次查询。     我们再看一下sys.dm_exec_cached_plans中的查询计划: Select Count(*) CNT,sum(size_in_bytes) TotalSize From sys.dm_exec_cached_plans
        查询结果:共有4条执行计划被缓存。它们共占用内存: 172032字节 = 168KB。     如果执行dbcc memorystatus,则得到结果:     有12875页空闲内存(每页8KB)可以被数据缓存所使用。     到这里,我们已经看到了一个反差相当明显的结果。在现实中,这个例子中的前者,正是经常被使用的一种执行SQL脚本的方式(例如:在程序中通过合并字符串方式拼成一条SQL语句,然后通过ADO.NET或者ADO方式传入SQL Server执行)。 
        解释一下原因:     我们知道,SQL语句在执行前首先将被编译并通过查询优化引擎进行优化,从而得到优化后的执行计划,然后按照执行计划被执行。对于整体相似、仅仅是参数不同的SQL语句,SQL Server可以重用执行计划。但对于不同的SQL语句,SQL Server并不能重复使用以前的执行计划,而是需要重新编译出一个新的执行计划。同时,SQL Server在内存足够使用的情况下,此时并不主动清除以前保存的查询计划(注:对于长时间不再使用的查询计划,SQL Server也会定期清理)。这样,不同的SQL语句执行方式,就将会大大影响SQL Server中存储的查询计划数目。如果限定了SQL Server最大可用内存,则过多无用的执行计划占用,将导致SQL Server可用内存减少,从而在执行查询时尤其是大的查询时与磁盘发生更多的内存页交换。如果没有限定最大可用内存,则SQL Server由于可用内存减少,从而会占用更多内存。    对此,我们一般可以通过两种方式实现参数化查询:一是尽可能使用存储过程执行SQL语句(这在现实中已经成为SQL Server DBA的一条原则),二是使用sp_executesql 方式执行单个SQL语句(注意不要像上面的第一个例子那样使用sp_executesql)。     在现实的同一个软件系统中,大量的负载类型往往是类似的,所区别的也只是每次传入的具体参数值的不同。所以使用参数化查询是必要和可能的。另外,通过这个例子我们也看到,由于使用了参数化查询,不仅仅是优化了SQL Server内存占用,而且由于能够重复使用前面被编译的执行计划,使后面的执行不需要再次编译,最终执行10000次查询总共只使用了1秒钟时间。     2、检查并分析SQL Server执行缓存中的执行计划 
        通过上面的介绍,我们可以看到SQL缓存所占用的内存大小。也知道了SQL Server执行缓存中的内容主要是各种SQL语句的执行计划。则要对缓存进行优化,就可以通过具体分析缓存中的执行计划,看看哪些是有用的、哪些是无用的执行计划来分析和定位问题。     通过查询DMV: sys.dm_exec_cached_plans,可以了解数据库中的缓存情况,包括被使用的次数、缓存类型、占用的内存大小等。 SELECT usecounts, cacheobjtype, objtype,size_in_bytes, plan_handle FROM sys.dm_exec_cached_plans     通过缓存计划的plan_handle可以查询到该执行计划详细信息,包括所对应的SQL语句: SELECT TOP 100 usecounts, objtype, p.size_in_bytes, [sql].[text] FROM sys.dm_exec_cached_plans p OUTER APPLY sys.dm_exec_sql_text (p.plan_handle) sql
    ORDER BY usecounts
            我们可以选择针对那些执行计划占用较大内存、而被重用次数较少的SQL语句进行重点分析。看其调用方式是否合理。另外,也可以对执行计划被重复使用次数较多的SQL语句进行分析,看其执行计划是否已经经过优化。进一步,通过对查询计划的分析,还可以根据需要找到系统中最占用IO、CPU时间、执行次数最多的一些SQL语句,然后进行相应的调优分析。篇幅所限,这里不对此进行过多介绍。读者可以查阅联机丛书中的:sys.dm_exec_query_plan内容得到相关帮助。 
      

  7.   

    MSSQL出于性能考虑不会自动释放已经使用的内存,而是全部留着,除非你重启SQL服务。
    或者设置为SQL最大内存即可。
    如果你的访问量很大,使用SQL查询很多,那就没有必要去释放这些内存,否则SQL又要向OS请求内存,反而更慢。