海量数据查询及分页优化(作 者: jyk 摘自:csdn)
http://community.csdn.net/Expert/TopicView3.asp?id=4180563
http://www.jyklzz.com/bbs/ 演示页面
感谢大家的支持!!!
昨天发了一个邀请,邀请大家帮忙测试,效果还可以,下面小结一下:
通过内部的计数器得知:访问次数是1071(其中有好多是自己点的:)),人数不是太理想,本来是想看看上万人同时访问的情况:)
系统资源的占用情况
内存 —— 很理想。SQL占用的内存最大也没有超过65M,一般是在35M左右;asp.net占用的内存最大也没有超过40M,一般是在25M左右。
CPU:8%左右,由于访问次数不多,也不够集中,所以这个数值也说明不了什么。自己连续点了n次下一页,发现CPU的使用率飘高,达到了50%左右。
但是对于100万的记录,AMD XP2000+ 的CPU 几十毫秒的放映速度,因该是可以接受的,甚至是很理想的吧。
毕竟服务器的CPU要比我的快很多吧,而且记录也很难达到100万吧。
结果还是很满意的,但是美中不足的是,我想看一下海量访问的情况下的效果,
希望大家再支持一下,多点几下,谢谢了。呵呵
另外说明一下:前n页可以在60毫秒内完成,n应该是大于500的,小于多少嘛还没有测试。后n页就比较慢了,需要500毫秒左右。
下面讨论一下翻页的技巧吧。
我没有用游标、临时表、not in、in 这些方法,并不是说他们的效率不高,而是我还没有测试过。我只用了 top ,查了两次表。
大家也可提供一些其他的方法,我来测试一下,看看在100万条的情况下的效果。(请不要给在存储过程里面组串的,看着实在是太费劲了)
讨论的前提是在海量数据的情况下,至少是在10万以上的。如果是很少的数据呢,那怎么翻都可以了。也差不了多少。
1.设置合理的索引
首先要做的是设置合理的索引,这个好像经常被忽略,至少很少被谈起。
注意:主键是索引的一种,而且是最快的一种。如果你都是把组件当作排序字段的话,那么你已经利用了索引。
不设置合理的所引的话,会导致查询速度非常的慢,甚至会造成超时。
这方面你可以做一个实验:找一个表,填进去10万条记录,假设有ID 、addedDate等字段,在查询分析器里面执行一下
select top 10 * from table
应该立刻就能出现结果。
然后再执行 select top 10 * from table order by ID(这时ID字段是主键)
也是立刻就出现了结果。
然后再执行 select top 10 * from table order by addedDate (这时addedDate字段没有索引)
你会发现速度很慢。
现在给addedDate 加一个非聚集索引,然后在执行上面的查询语句,速度也变得很快了。
可见索引神奇的效果!
这是翻动百万级记录最基本的设置,具体到我的那个论坛的翻页,我是设置了BoardID、 replyDate两个字段作为联合索引的。
因为是要在同一个讨论组李翻页,而且是按replyDate排序的。
2.只返回需要的记录
对于海量数据,都读出来做缓存,那是不可想象的(记录少的话,也要看利用率,一般都是很浪费的)。
所以呢如果一页显示20条的话名那就只都读出来20条,这样就很省内存和时间。
注意:虽然ADO.NET里面有这个方法
SqlDataAdapter.Fill(DataSet1,startRecord,maxRecords,srcTable);
但是他还是要先从SQL里面把查询语句的查出来的所有记录都出来,然后在截取指定的记录数。这对于SQL来说是一样的,对于海量数据依然会很慢。
论坛里的首页用的是select top 20 * from table where boardID = 5 order by replyDate desc
这样呢就只返回了20条记录,再加上索引的功劳,速度是非常快的。
3.尽量减少字段的长度
一个表可以建很多的字段,但是字段的总长度不能超过8060B,也就是说如果你建了一个char(8060)的字段,就不能在建其他的字段了。
我在第一次的测试中(星期天的),把主题的所有信息都放在了一个表里面,包括了一个nvarchar(3600)的主题内容的字段,复制记录的时候发现非常的慢,
当达到9万的时候,就已经很慢的,勉强把记录数拷贝到了35万,加了索引,测试了一下,翻页速度还是可以的,前n也都是很快的,后n页就很慢了,
如果再加上查询那就非常之慢了。
查看了一下数据文件吓了一跳 —— 他居然占用了1.4G的硬盘空间,怪不得拷贝和查询都慢的要死呢。
于是修改了一下表结构,把那个nvarchar(3600)的主题内容的字段踢了出去,放在一个单独的表里面。
再重新拷贝记录就非常的快了,很快就把记录数从16表成了1048577。昨天的测试就是在这个条件下进行的。
4.技巧
终于到了翻页算法的地方了,呵呵没有等急吧。
思路呢就是先找到一个标志,然后呢把大于(或小于)这个标志的前n条记录取出来。
什么?没看懂。没关系,我举个例子吧。
假设是按ID倒序的,每一页显示10条记录,有100条记录,记录号正好是1到100(怎么这么巧??为了说明方便嘛)
那么第一页的记录就是100到91、第二页的记录就是90到81、第三页的记录就是80到71......
我现在要翻到第三页,那么要找到第21行的记录的ID的值(也就是80),然后把小于等于80的记录用top 10 取出来就行了。
查询语句
declare @pageSize int --返回一页的记录数
declare @CurPage int --页号(第几页)1:第一页;2:第二页;......;-1最后一页。
declare @Count int
declare @id int
set @pageSize=10
set @CurPage =1
if @CurPage = -1
begin
--最后一页
set rowcount @pageSize
select @id=ID from table order by ID
end
--定位
if @CurPage > 0
begin
set @Count = @pageSize * (@CurPage -1) + 1
set rowcount @Count
select @id=ID from table order by ID desc
end
--返回记录
set rowcount @pageSize
select * from table where ID <=@id order by ID desc
set rowcount 0
其中“定位”用了 select @id=ID from table order by ID desc
这种方法,感觉上是很省内存的,因为只记录了一个ID,
然后用 select * from table where ID <=@id order by ID desc
取得最终需要的记录
set rowcount @pageSize 相当于 top @pageSize 。
优点:无论翻到哪一页,内存的占用情况都不变,多人访问内存也不会不变,很多人呢,还没有测试出来:)
缺点:单表、单排序字段。
主 题: 翻动100万级的数据 —— 只需几十毫秒 之揭秘篇:有详细的说明,不要错过。
作 者: jyk (喜欢编程。和气生财。共同提高。共同进步)