6,ORM组件XCode(撬动千万级数据)

    有了前面的《动手》,基本上能够进行开发了。本篇咱们来试试XCode的基本功功力如何,测试在单表一千万业务数据的环境下查询的速度,添删改等没什么可测试的。其实应该说是XCode开发模式的功力,XCode组件仅仅是处理分页而已,而XCode开发模式为高性能开发提供了更多的建议。 html

    测试环境:双核CPU,4G内存,win7+SQL2008+vs2010 sql

    数据表字段包括:自增ID、车牌、时间。使用SQL准备一千万测试数据,花了将近一个小时。 数据库

    测试用例:ID的升序降序,时间的升序降序,每一种状况测试取首页、中间页、尾页的时间。 架构

    XCode开发模式很是看重分页,基本上全部集合查询方法都带有分页参数。Entity层只负责生成获取知足条件的全部数据的SQL,加上分页参数后传递给下层数据访问层,自身不处理问题。数据访问层调用IDatabase接口的PageSplit方法,把上述的SQL处理为只获取指定页的SQL,而后再执行查询操做。由于不一样的数据库分页方法不一样,因此XCode的这种架构让使用者无需关心采用哪种分页方法。测试环境是SQL2008,因此自动采用row_number分页。 ide

 

    首先创建数据表 工具

代码
CREATE   TABLE   [ dbo ] . [ test ] (
    
[ ID ]   [ int ]   IDENTITY ( 1 , 1 NOT   NULL ,
    
[ HPHM ]   [ varchar ] ( 50 NULL ,
    
[ JGSJ ]   [ datetime ]   NOT   NULL ,
 
CONSTRAINT   [ PK_CLTXJL ]   PRIMARY   KEY   CLUSTERED  
(
    
[ ID ]   DESC
)
WITH  (PAD_INDEX   =   OFF , STATISTICS_NORECOMPUTE   =   OFF , IGNORE_DUP_KEY  =   OFF , ALLOW_ROW_LOCKS   =   ON , ALLOW_PAGE_LOCKS   =   ON ON   [ PRIMARY ]
ON   [ PRIMARY ]

 

     使用SQL语句插入一千万行数据 性能

declare   @i   int
set   @i = 0
while   @i < 100
begin
insert   into  test  values ( ' 鄂A94450 ' , getdate ())
insert   into  test  values ( ' 鄂A92355 ' , getdate ())
(这里是更多数据插入语句)
set   @i = @i + 1
end
GO

 

    最后是这种样子 测试

wps_clip_image-18008

    再看看咱们准备的测试代码 网站

测试代码
static   void  Test2()
{
    Stopwatch sw 
=   new  Stopwatch();
    sw.Start();

    Console.WriteLine();
    DAL.AddConnStr(
" Center " " Data Source=.;Initial Catalog=Center;User ID=sa;Password=Pass@word " null " sql2008 " );
    IEntityOperate factory 
=  DAL.Create( " Center " ).CreateOperate( " test " );
    sw.Stop();
    Console.WriteLine(
" 初始化:{0} " , sw.Elapsed);

    ICollection list 
=  factory.FindAll( null null null 100000 1 );
    DateTime dt 
=  DateTime.Now;
    
foreach  (IEntity item  in  list)
    {
        dt 
=  (DateTime)item[ " JGSJ " ];
        
break ;
    }

    
// String where = String.Format("{0}>='{1}' And {0}<'{2}'", "JGSJ", dt, dt.AddSeconds(100));
    String  where   =  String.Empty;

    sw.Reset();
    sw.Start();
    Console.WriteLine();
    Int32 count 
=  factory.FindCount( where null null 0 0 );
    sw.Stop();
    Console.WriteLine(
" 查询总记录数:{0} " , sw.Elapsed);
    Console.WriteLine(
" 总记录数:{0} " , count);

    Test2_0(sw, 
" 默认顺序 " , count,  where null );
    Test2_0(sw, 
" 时间升序 " , count,  where " JGSJ Asc " );
    Test2_0(sw, 
" 时间降序 " , count,  where " JGSJ Desc " );
}

///  
///  测试用例
///  
///   计时器
///   标题
///   总记录数
///   条件字句
///   排序字句
static   void  Test2_0(Stopwatch sw, String title, Int32 count, String  where , String order)
{
    Console.WriteLine();
    Console.WriteLine(
" {0}: " , title);

    IEntityOperate factory 
=  DAL.Create( " Center " ).CreateOperate( " test " );

    sw.Reset();
    sw.Start();
    Console.WriteLine();
    ICollection list 
=  factory.FindAll( where , order,  null 0 10 );
    sw.Stop();
    Console.WriteLine(
" 查询前10行:{0} " , sw.Elapsed);

    sw.Reset();
    sw.Start();
    Console.WriteLine();
    list 
=  factory.FindAll( where , order,  null , count  /   2 10 );
    sw.Stop();
    Console.WriteLine(
" 中间10行({1}):{0} " , sw.Elapsed, count  /   2 );

    sw.Reset();
    sw.Start();
    Console.WriteLine();
    list 
=  factory.FindAll( where , order,  null , count  -   10 10 );
    sw.Stop();
    Console.WriteLine(
" 最后10行({1}):{0} " , sw.Elapsed, count  -   10 );
}

 

    上面第一个方法是控制方法,用来控制测试用例的。第二个方法就是测试用例了。 spa

    在这里不得不提的是,第一个方法使用了最新版本V5.0的新特性——弱类型访问。上一篇《动手》中提到,使用XCode首先须要利用代码生成器生成实体类代码,或者手工编写,反正是须要实体类代码,而本文只是为了测试,不须要那么复杂。动态添加一个链接字符串Center,并建立数据表test的操做接口,后面就能够利用这个操做接口去查询数据了。弱类型访问这一块后面会专门介绍。

    第二个方法有三次查询,分别是首页、中间页和尾页。

    先来看看“默认顺序”,其实就是ID降序

wps_clip_image-27189

    由于数据表默认为自增ID创建汇集索引,因此在ID字段上的分页查询是最快的,首页才3毫秒,中间页也才4.5秒。

这里有必要说一下尾页,这里不是做弊,而是XCode的一个小手段。在实际应用分页查询的时候,每每是越日后越慢,但只要把数据倒过来查,ID降序的尾页其实就是ID升序的首页,结果行集一致,只不过这10行数据是倒过来的,XCode在最后返回实体集合的时候会把它倒过来,就成了ID降序了。因此,在XCode查询中,中间页之后的页都是反向查询,中间页是最慢的。

    接着看看“时间升序”

wps_clip_image-27635

    首页和尾页5秒,中间页17秒,很糟糕!看一下它们的执行计划
wps_clip_image-31722
wps_clip_image-13542
wps_clip_image-5627

    习惯性的先看总开销,三条语句竟然是势均力敌,执行时间一致!这个我就没法解释了。

    从执行计划能够看到,95%的开销都在于排序

wps_clip_image-14991

    看详情,原来是对JGSJ的排序形成的。看来应该为JGSJ创建索引。

    最后的这个“时间降序”,时间跟“时间升序”差很少,原理也同样,就不分析了。

wps_clip_image-29070

    上次第一轮测试看到,没有索引,实在杯具!下面咱们给JGSJ字段加上索引,继续测试

wps_clip_image-10701

wps_clip_image-18189

wps_clip_image-17478

    有了明显变化,首页和尾页足够快了,中间页也变快了,可是仍是偏慢,怎么回事?从执行计划看到,99%的时间都在于键查找

wps_clip_image-6367

    原来是查找对应的HPHM,也是,索引只负责时间字段,而HPHM字段仍是须要作全表扫描找出来的。在索引里面包含它试试

CREATE   NONCLUSTERED   INDEX  IX_test_1  ON  dbo.test
(
  JGSJ
)
include (HPHM)
WITH ( STATISTICS_NORECOMPUTE  =   OFF , IGNORE_DUP_KEY  =   OFF , ALLOW_ROW_LOCKS  =   ON , ALLOW_PAGE_LOCKS  =   ON ON   [ PRIMARY ]

 

wps_clip_image-5301

    再次测试

wps_clip_image-22784

    漂亮!结果跟ID自增字段同样。

    综合上面的测试,最慢的中间页能保持在5秒之内,算是一个不错的成绩了。不过这不能彻底算是XCode的功劳,XCode仅仅是生成了分页语句而已。而创建索引的建议,则是XCode开发模式的范畴。

 

    XCode开发模式建议:每一个表使用自增ID做为主键,独享汇集索引。在数据分页上,没有比自增ID加上汇集索引更快的了,因此要把最好的留给它。业务主键还有常常查询的字段,根据状况创建非汇集索引。在千万数据下,没有索引的字段,基本上查不动。

    创建索引时,特别注意包含字段include(不是组合索引)。好比为时间字段创建了索引,根据时间字段查询的时候,扫描索引字段会很快,可是扫描以后绝大部分时间都花在查找时间字段对应的车牌字段上了,若是创建时间字段索引的时候,把车牌字段include进去,就至关于在索引目录里面就拥有了车牌信息,直接省去了对应车牌这一步,查询性能将会获得很是大的提升。固然,include也是有代价的,添删改操做会比原来慢,而且要占用更大的存储空间。不过如今硬盘那么便宜,存储空间问题不会太大,至于添删改操做慢多少,就看业务来衡量了,通常能够接受。

    在SQLServer管理工具里面创建索引时,彷佛没法添加include字段。能够先设置好索引,不要保存,点击生成脚本,而后复制到查询窗口,增长include后再执行。

 

    前面的测试,都是简单的没有查询条件的测试,下面咱们试试带查询条件的测试

wps_clip_image-13996

    屏幕一闪而过,就这样完了!图中看到,符合条件的数据共有2317+10=2327条,在这么小的数据量里进行分页查询,那速度,天然没得说!

    在实际应用中,不多有须要查询那么多页的,百度、谷歌和淘宝等大型网站,最多也就返回前面一百页。而且,业务系统通常有不少查询条件,好比时间段等,通过这些条件过滤,即便是千万数据的表,也不会有太多知足条件的数据

 

这一切,XCode已经为你准备!

 

大石头

新生命开发团队

2010-09-07 03:57

 

组件文档打包下载(1~6)

做者: 大石头 发表于 2010-09-15 23:28 原文连接

评论: 23 查看评论 发表评论

最新新闻:
· 是否该让开发人员跟客户直接交流?(2010-12-21 07:53)
· 亚马逊副总裁跳槽Groupon任CFO(2010-12-21 07:50)
· Gmail语音服务将延长至2011年末(2010-12-21 07:49)
· AOL收购我的档案网站About.me(2010-12-21 07:48)
· AT&T 19.25亿美圆购入高通 700MHz 低频频谱(2010-12-21 07:42)

编辑推荐:Mono又更新了

网站导航:博客园首页  个人园子  新闻  闪存  小组  博问  知识库

相关文章
相关标签/搜索