【原创】项目性能优化全纪录(一) 存储过程优化

    公司的项目中需要增加一个对信息亭点击量进行实时统计的子项目。前台程序点击的Log数据会被保存到服务器上的一张AppLog表中。

信息亭每天的点击量很高,可想而知AppLog表中数据量得有多大。据初步估算,一年可以产生上亿条点击纪录。因而数据统计的性能问题一直困扰着我。

七夕节在家没事干,就看了一下微软的WebCast中asp.net优化的那部分视频同时还看了一本Richhard Kiessing写的《构建高性能可扩展Asp.net网站》。

星期一一上班就开始着手对项目进行优化。

    废话不多说先看Applog的表结构:



所作的统计基本上都是根据EventTime来统计Logcode的数量。其中有一个最耗费性能的统计就是根据某一个时间段按照用户给出的统计条件来统计点击数量。

项目第一个版本的存储过程是这样写的:

 1 use CMS
2 if Exists(select name from sysobjects where name='GetGroupClicksByDate' And type='p')
3 drop procedure dbo.GetGroupClicksByDate
4 go
5
6 create proc dbo.GetGroupClicksByDate
7 @date_begin datetime,
8 @date_end datetime,
9 @int_interval int,
10 @var_projectName varchar(32)
11 as
12 declare @int_datecount int
13 if @date_begin is not null and @date_end is not null and @int_interval is not null and @var_projectName<>''
14 begin
15 set @int_datecount= datediff(hh,@date_begin,@date_end)
16 if @int_datecount % @int_interval <>0
17 set @date_end=DATEADD(HOUR, ((@int_datecount/@int_interval)+1)*@int_interval,@date_begin)
18 select r.StartTime,r.EndTime, ISNULL(count(t.LogCode),0) as SumCount
19 from applog t
20 right join
21 (
22 select
23 h.LogCode,
24 DATEADD(HOUR,number*@int_interval,@date_begin) as StartTime,
25 DATEADD(HOUR,(number+1)*@int_interval,@date_begin) as EndTime
26 from master..spt_values,
27 (select distinct LogCode
28 from applog
29 where applog.ProjectName=@var_projectName )h
30 where type='P' and
31 DATEADD(HOUR,(number+1)*@int_interval,@date_begin)<=@date_end
32
33 ) r
34 on t.LogCode =r.LogCode and t.EventTime >= r.StartTime and t.EventTime< r.EndTime
35
36 group by r.StartTime,r.EndTime
37 order by r.StartTime
38 end

  其原理是通过master数据库生成一张时间段的临时表,然后再利用Applog表和master表进行右连接之后,在进行分组统计查询。

我统计一次从2010年1月1日到2011年11月2日,SQLServer执行的时间是1分51.555秒。结果如下:

    通过思考,我发现可以先根据时间段来拆分SQL语句,然后使用union all在连接起来查询,这样的话可以减少运算3次笛卡尔积性能必定能够提升。其具体实现如下:

use CMS
if Exists(select name from sysobjects where name='GetGroupClicksByDate' And type='p')
drop procedure dbo.GetGroupClicksByDate
go
/*
Written By:WMJ
WriteDate :2011-08-19
Function: Get the clicks by time interval
*/
create proc dbo.GetGroupClicksByDate
@date_begin datetime,
@date_end datetime,
@int_interval int,
@var_projectName varchar(32)
as

declare @var_SQL varchar(MAX)
declare @int_datecount int
declare @int_datecal int
declare @date_temptime datetime
if @date_begin is not null and @date_end is not null and @int_interval is not null and @var_projectName<>''
begin
set @int_datecount= datediff(hh,@date_begin,@date_end) --Calcultate the total hours count
set @int_datecal=@int_datecount/@int_interval --Calculate the total datarangge count
if @int_datecount % @int_interval <>0
begin
set @date_end=DATEADD(HOUR, ((@int_datecount/@int_interval)+1)*@int_interval,@date_begin)
end
set @date_temptime=DATEADD(HOUR,@int_interval,@date_begin)
set @var_SQL='select count(*) as Sumcount,'''+CONVERT(varchar, @date_begin,120)+''' as StartTime,'''+CONVERT(varchar,@date_temptime ,120)+''' as EndTime from applog where applog.EventTime> '''+CONVERT(varchar, @date_begin,120)
set @date_begin=@date_temptime
set @var_SQL=@var_SQL+ ''' and applog.EventTime <='''+CONVERT(varchar, @date_begin ,120)+''' and applog.ProjectName= '''+@var_projectName+''' '
while @int_datecal>0
begin

set @date_temptime=DATEADD(HOUR,@int_interval,@date_begin)
set @var_SQL=@var_SQL+' union all '+' select count(*) as Sumcount,'''+CONVERT(varchar, @date_begin,120)+''' as StartTime,'''+Convert(varchar,@date_temptime,120)+''' as EndTime from applog where applog.EventTime> '''+CONVERT(varchar, @date_begin,120)
set @date_begin=@date_temptime
set @var_SQL=@var_SQL+ ''' and applog.EventTime <='''+CONVERT(varchar, @date_begin ,120)+''' and applog.ProjectName= '''+@var_projectName+''' '
set @int_datecal=@int_datecal-1
end
end
exec( @var_SQL)--Exexute the sql

  事实证明我的想法是对的,执行同样的查询得到相同的结果SQL Server只需要1.319秒,性能提升了98.82%.如下图所示:

    通过这次成功的优化,我发现做写程序做项目不仅仅是把项目完成就OK了。旧项目里面还有很多东西需要考虑和优化,只有不断的优化才能使项目运行的更稳定才能使自己得到更大的提升,这就是“温故而知新”的重要意义吧。

posted @ 2011-08-09 14:24  水晶泡泡  阅读(356)  评论(0编辑  收藏  举报