现在的位置: 首页 > 综合 > 正文

在论坛中出现的各种疑难问题:性能优化

2014年04月05日 ⁄ 综合 ⁄ 共 3336字 ⁄ 字号 评论关闭

 

1、模糊匹配

关于2个表模糊搜索匹配的问题,现已找到较快的解决方法,速度提升到每秒5条记录左右,而且不占CPU,不占内存,方法如下:
-------------------------------------------------------------------------
环境:
 有2个表,
 表1:MainTable (现有记录数在10万条左右)
字段:
id  bigint自动编号
Title nvarchar(30)
 SubId nvarchar(max)

 

表MainTable:
Id Title SubId
 1  A       0
 2  B       0
 3  C       0
 4  D       0
 5  E       0
 6  F       0

 

表2:SubTable (现有记录数在300万条左右)
 字段:
id bigint自动编号
Description nvarchar(100)
 Fl int '默认值为0,当进行模糊匹配后,值改为1

表SubTable:
Id Description Fl
 1  ABC         0
 2  AB          0
 3  CD          0
 4  EA          0

 

二、需要实现的结果为:
 表MainTable:
Id Title SubId
 1  A     0,1,2,4
 2  B     0,1,2
 3  C     0,1,3
 4  D     0,3
 5  E     0,4
 6  F     Null

 

第6条记录由于没有匹配的值,所以改为Null
 -------------------------------------------------------------------------

1、由于记录数太多,通过内部存储搜索速度太慢,中途不能暂停,平均每分钟才能处理5条记录左右;
2、原先通过外部循环的方法处理,速度1秒1条左右,会比方法1速度快,但非常占CPU;

 

现在的办法:
 用VBS编写,定义2个数组,IDArray()和DescriptionArray()分别用于存储在SubTable表检索到的ID集和Description集

1、MainTable用循环的方式,按字段Title升序的方式得取Title字段的值,
 先取得第一条记录的Title字段的第一个字符,根据这个字符模糊匹配SubTable的Description字段,并将检索到的结果存放在数组IDArray和DescriptionArray()中。
2、通过循环方式,将MainTable表的当前记录的Title字段的完整值与DescriptionArray()的值进行匹配处理,并update。

3、获取MainTable的下一条记录,判断该条记录Title字段的第一个字符是否与上一条记录的Title的第一个字符相同,如果相同,则从第2步开始处理;如果不同,则从第1步开始处理。

------------------------------------------------------------------------------
这种方法减少了每次去SubTable模糊搜索的次数,如果Title字段的第一个字符相同的记录非常多的情况下,速度还可能会提高很多。

 

总结一下,这个问题的解决不是通过sql server,而是在vbs,通过运用数据本身的特性,也就是:Title字段的第一个字符相同的记录非常多,减少了重复的劳动,少做了很多的无用功,最后,大幅提升性能。

真的是好办法,其实,从这个例子中可以看出,优化,更重要的是强调思维,而不简单的是某个技术,注重细节,仔细分析,楼主就解决了这个优化问题。

2、分页查询

http://bbs.csdn.net/topics/390702739

有个日志表,测试数据200万条,用的分页查询,每次查询50条记录,表里创建了非聚集索引,查询前几页会很快,基本在1秒内,如果突然查询表的最后的数据,也就是最后一页,则需要花费半分钟的时候,请问这是为什么呢?有没有办法优化一下呢?
表结构如下:

CREATE NONCLUSTERED INDEX [_dta_index_loga_11_69575286__K1_2_3_4_5] ON [dbo].[loga] 
(
	[DateTime] ASC
)
INCLUDE ( [ServerName],
[ApplicationName],
[InfoLevel],
[msg]) WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
GO


ALTER TABLE [dbo].[loga] ADD  CONSTRAINT [PK_loga] PRIMARY KEY CLUSTERED 
(
	[ServerName] ASC,
	[ApplicationName] ASC,
	[DateTime] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
GO

查询语句:

With info 
AS 
(
SELECT [datetime],servername,applicationname,
       infolevel,msg,
       ROW_NUMBER() OVER (order by [datetime]) as RowNumber 
FROM loga
) 

select * 
from info 
Where RowNumber between 500 and 1000

select count(*) from loga


以上两个语句,第一个语句,运行速度不稳定,在查询最后一个分页时需要30秒左右,而查询前几页的时候,基本上是0秒。

而第二个语句的运行速度非常慢。


下面是执行计划,分别是count(*) 、查询前两页、查询最后一页的:


其实,从上面的后两个执行计划来看,是一样的,应该是没什么区别的,一般意义上的理解,那么不管查询前两页,还是查询最后一页,应该是一样快的,都应该0秒就出来的,而且确实从执行计划上,也说明了这一点,但实际上两者的差异却很大,这是为什么呢?


仔细查看发现,执行计划中使用了索引扫描,那么既然是扫描,那为什么前两页那么快,而最后一页那么慢呢?

我觉得是这样的,扫描就像是翻书一样,从第一页开始,由于第一个是只需要看第二页,也就是500-1000的500条数据,也就是第二页,那么当sql server 翻到第二页的时候,取到了需要的数据,就停止了继续查看后面的数据了,于是查询结束了,这就是只需要0秒的原因。


而后一个,是最后一页,也就相当于一本书的最后一页,当然啦,sql server并不知道最后的500条数据,到底是准确的处于哪些数据页中(这个并不像书那么简单),于是只能从第一页开始查看,直到最后一页,取出最后的500行数据,那么就会需要消耗30多秒了,那么怎么优化呢?


我的想法是,把语句修改一下,先计算表中总的条数,然后看要取的条数,位于整个表中的前半部分,还是后半部分,如果是后半部分,那么把语句改成这样:

With info 
AS 
(
SELECT [datetime],servername,applicationname,
       infolevel,msg,
       ROW_NUMBER() OVER (order by [datetime] desc ) as RowNumber 
FROM loga
) 

select * 
from info 
Where RowNumber between 1 and 500
order by datetime 

修改后,取最后一页,只需要0秒。

而count(*) 可以通过再建一个索引,来加快速度,通过建立如下的索引,count(*)只需要0秒就出来了:

create index idx_loga_datetime on loga(datetime)

抱歉!评论已关闭.