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分頁。
首先建立數據表
代碼
[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語句插入一千萬行數據
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
最后是這種樣子
再看看我們準備的測試代碼
測試代碼
{
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降序
因為數據表默認為自增ID建立聚集索引,所以在ID字段上的分頁查詢是最快的,首頁才3毫秒,中間頁也才4.5秒。
這里有必要說一下尾頁,這里不是作弊,而是XCode的一個小手段。在實際應用分頁查詢的時候,往往是越往后越慢,但只要把數據倒過來查,ID降序的尾頁其實就是ID升序的首頁,結果行集一致,只不過這10行數據是倒過來的,XCode在最后返回實體集合的時候會把它倒過來,就成了ID降序了。所以,在XCode查詢中,中間頁以后的頁都是反向查詢,中間頁是最慢的。
接著看看“時間升序”
習慣性的先看總開銷,三條語句居然是平分秋色,執行時間一致!這個我就無法解釋了。
從執行計劃可以看到,95%的開銷都在于排序
看詳情,原來是對JGSJ的排序造成的。看來應該為JGSJ建立索引。
最后的這個“時間降序”,時間跟“時間升序”差不多,原理也一樣,就不分析了。
上次第一輪測試看到,沒有索引,實在杯具!下面我們給JGSJ字段加上索引,繼續測試
有了明顯變化,首頁和尾頁足夠快了,中間頁也變快了,但是還是偏慢,怎么回事?從執行計劃看到,99%的時間都在于鍵查找
原來是查找對應的HPHM,也是,索引只負責時間字段,而HPHM字段還是需要做全表掃描找出來的。在索引里面包含它試試
(
JGSJ
)
include (HPHM)
WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
再次測試
漂亮!結果跟ID自增字段一樣。
綜合上面的測試,最慢的中間頁能保持在5秒以內,算是一個不錯的成績了。不過這不能完全算是XCode的功勞,XCode僅僅是生成了分頁語句而已。而建立索引的建議,則是XCode開發模式的范疇。
XCode開發模式建議:每個表使用自增ID作為主鍵,獨享聚集索引。在數據分頁上,沒有比自增ID加上聚集索引更快的了,所以要把最好的留給它。業務主鍵還有經常查詢的字段,根據情況建立非聚集索引。在千萬數據下,沒有索引的字段,基本上查不動。
建立索引時,特別注意包含字段include(不是組合索引)。比如為時間字段建立了索引,根據時間字段查詢的時候,掃描索引字段會很快,但是掃描之后絕大部分時間都花在查找時間字段對應的車牌字段上了,如果建立時間字段索引的時候,把車牌字段include進去,就相當于在索引目錄里面就擁有了車牌信息,直接省去了對應車牌這一步,查詢性能將會得到非常大的提高。當然,include也是有代價的,添刪改操作會比原來慢,并且要占用更大的存儲空間。不過現在硬盤那么便宜,存儲空間問題不會太大,至于添刪改操作慢多少,就看業務來衡量了,一般可以接受。
在SQLServer管理工具里面建立索引時,似乎無法添加include字段。可以先設置好索引,不要保存,點擊生成腳本,然后復制到查詢窗口,增加include后再執行。
前面的測試,都是簡單的沒有查詢條件的測試,下面我們試試帶查詢條件的測試
屏幕一閃而過,就這樣完了!圖中看到,符合條件的數據共有2317+10=2327條,在這么小的數據量里進行分頁查詢,那速度,自然沒得說!
在實際應用中,很少有需要查詢那么多頁的,百度、谷歌和淘寶等大型網站,最多也就返回前面一百頁。并且,業務系統一般有很多查詢條件,比如時間段等,經過這些條件過濾,即使是千萬數據的表,也不會有太多滿足條件的數據。
這一切,XCode已經為你準備!
大石頭
新生命開發團隊
2010-09-07 03:57


浙公網安備 33010602011771號