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

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

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

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

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

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

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

 

    首先建立数据表

6,ORM组件XCode(撬动千万级数据)
ExpandedBlockStart.gif
代码

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

    再看看我们准备的测试代码

6,ORM组件XCode(撬动千万级数据)
ExpandedBlockStart.gif
测试代码

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

);
}


///
 
<summary>


///
 测试用例

///
 
</summary>


///
 
<param name=”sw”>
计时器
</param>


///
 
<param name=”title”>
标题
</param>


///
 
<param name=”count”>
总记录数
</param>


///
 
<param name=”where”>
条件字句
</param>


///
 
<param name=”order”>
排序字句
</param>


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
);
}

 

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

    在这里不得不提的是,第一个方法使用了最新版本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)

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/110720.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • 面试了一个5年的Java,问什么都说不会…!

    面试了一个5年的Java,问什么都说不会…!最近看了很多简历,很多候选人年限不小,但是做的都是一些非常传统的项目,想着也不能通过简历就直接否定一个人,何况现在大环境越来越难,大家找工作也不容易,…

  • springboot框架简介优点_springboot框架的优点

    springboot框架简介优点_springboot框架的优点1.1.什么是SpringBootSpringBoot是Spring项目中的一个子工程,与我们所熟知的Spring-framework同属于spring的产品:我们可以看到下面的一段介绍:Takesanopinionatedviewofbuildingproduction-readySpringapplications.SpringBootfavorsconventionoverconfigurationandisdesignedtogetyouupa

  • linux系统对硬盘分区_centos怎么分区

    linux系统对硬盘分区_centos怎么分区Linux操作系统磁盘分区操作及原理,Linux系统网络环境的配置方法。

  • Http请求URL长度限制[通俗易懂]

    Http请求URL长度限制[通俗易懂]http1.1协议原文http1.1协议中对url的长度是不受限制的,协议原文://https://www.ietf.org/rfc/rfc2616.txt3.2.1GeneralSyntax TheHTTPprotocoldoesnotplaceanyapriorilimitonthelengthofaURI.ServersM…

  • MOS管如何检测好坏[通俗易懂]

    MOS管如何检测好坏[通俗易懂]系列文章目录1.元件基础2.电路设计3.PCB设计4.元件焊接6.程序设计9.检测标准MOS管是金属—氧化物—半导体场效应晶体管,或称是金属—绝缘体—半导体。MOS管的source和drain是可以对调的,他们都是在P型backgate中形成的N型区。在多数情况下,这个两个区是一样的,即使两端对调也不会影响器件的性能。这样的器件被认为是对称的。双极型晶体管把输入端电流的微小变化放大后,在输出端输出一个大的电流变化。双极型晶体管的增益就定义为输出输入电流之比(beta)。另一种晶体管,叫

  • idea切换到远程分支_git看不到远程分支

    idea切换到远程分支_git看不到远程分支描述最近项目建了分支,在并行开发,需要切换远程分支,在网上找了很多资料都不行。远程分支代码始终更新的是其他分支。解决方案话不多说,直接看图。操作上述之后,开始pull下远程代码,完毕后也可发现分支发生变化说明以上只在2016.1.3上验证。

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号