一、起因
個人還是比較喜歡EF的,畢竟不用寫Sql,開發(fā)效率高,操作簡單,不過總是聽人說EF的性能不是很好,也看過別人做的測試,但是看了就以為真的是那樣。但是實際上到底是怎么樣,說實話我真的不知道。我只知道選什么的框架是基于實際情況的,博主在一個創(chuàng)業(yè)公司上班,選的就是EF框架,剛做了一個項目,數(shù)據(jù)也就幾萬不到,感覺性能沒那么差勁。于是,就想多弄點數(shù)據(jù)測試一下。再說一遍,本著 求真務實的方針,是針對現(xiàn)實中的業(yè)務需求來測試的,不是來單比性能的。你要是做個ERP系統(tǒng),都去考慮千萬級并發(fā)的架構(gòu),那當我沒說。畢竟不是基于實際項目的框架選擇都是耍流氓。
二、聲明
基于實際的項目,考慮到博主一般的遇到的上線項目對于數(shù)據(jù)的增刪改操作時,操作的數(shù)據(jù)一般都是一個,兩個,多了有十幾個,對于一下同時提交幾十個數(shù)據(jù)進行增刪改的,原諒博主還沒有見過,更有甚者,提交幾百個數(shù)據(jù)進行增刪改,博主想也是沒有想過。但是在這個數(shù)量級下的增刪改操作,我相信EF還是能夠勝任的,所以本文不再測試EF的增刪改性能,因為感覺完全能夠滿足一般項目的需要。本文只測試EF的單表查詢功能,之后有時間會做復雜的鏈接查詢的測試。
三、測試條件
老百姓的配置,自己的工作電腦。
Sql Server 2012,Entity Framework 6.1.3。
四、測試數(shù)據(jù)
鑒于以前看過的測試都是兩三個字段,且數(shù)據(jù)過于簡單,以防有這方面的影響,又因為實際項目中的字段可能較多,而且數(shù)據(jù)量也比較復雜,就模擬了一個較為接近的數(shù)據(jù)表,再說一遍,本著求真務實的革命主義方針,針對現(xiàn)實的項目來測試。
數(shù)據(jù)量100W
五、開始測試
做了一個WinForm的測試,界面如下
1.進行Find測試,隨機生成id,左邊顯示查詢用時,先上代碼。
1 private PortalContext db = new PortalContext(); 2 private int count = 0; 3 private TimeSpan ts = new TimeSpan(); 4 private void btnFind_Click(object sender, EventArgs e) 5 { 6 7 count++; 8 Random r = new Random(); 9 var id = r.Next(0, 1000000);10 txtId.Text = id.ToString();11 12 Stopwatch sw = new Stopwatch();13 sw.Start();14 var user = db.Users.Find(id);15 sw.Stop();16 17 txtUserInfo.Text = UserToString(user);18 ts += sw.Elapsed;19 string time = sw.Elapsed + "(" + sw.Elapsed.Seconds + "s" + sw.Elapsed.Milliseconds + "ms)";20 txtDisplay.AppendText("Find查詢id(" + id + ")用時:" + time + Environment.NewLine);21 txtData.Text = "執(zhí)行" + count + "次,平均耗時" + new TimeSpan((ts.Ticks / count));22 }
結(jié)果如下:
可以看出,在100w數(shù)據(jù)的情況下,利用Find根據(jù)主鍵id查詢根本無壓力,至于第一次很長時間,應該是連接數(shù)據(jù)花費了一些時間。
2.進行Where測試,代碼如下。
1 private void btnWhere_Click(object sender, EventArgs e) 2 { 3 4 5 bool[] valids = new bool[] { false, true }; 6 string[] works = new[] { "程序猿", "攻城獅", "產(chǎn)品汪", "鍵盤俠", "代碼狗" }; 7 UserType[] userTypes = new[] { UserType.合作方, UserType.普通用戶, UserType.律師 }; 8 Random r = new Random(); 9 10 int num = r.Next(0, 4680);11 int num2 = r.Next(0, 4680);12 13 int max = Math.Max(num, num2);14 int min = Math.Min(num, num2);15 16 bool isValid = valids[num % 2];17 string work = works[num % 5];18 UserType type = userTypes[num % 2];19 20 txtIsValid.Text = isValid.ToString();21 txtWork.Text = work;22 txtUserType.Text = type.ToString();23 txtAmountMin.Text = min.ToString();24 txtAmountMax.Text = max.ToString();25 26 Stopwatch sw = new Stopwatch();27 sw.Start();28 var query = db.Users.Where(u => true);29 var queryWhere = query.Where(u =>u.UserType == type &&u.IsValid == isValid && u.Work == work && (u.Amount >= min && u.Amount <= max)).Take(1000);30 var list = queryWhere.ToList();31 sw.Stop();32 33 labelWhere.Text = string.Format("where(u=> u.UserType=={0} && u.IsValid =={1} && u.Work == {2} u.Amount >= {3} && u.Amount <={4}).Take(1000)",34 type,isValid,work, min, max);35 36 string time = sw.Elapsed + "(" + sw.Elapsed.Seconds + "s" + sw.Elapsed.Milliseconds + "ms)";37 txtDisplay.AppendText("Where查詢到"+list.Count()+"條數(shù)據(jù),用時:" + time + Environment.NewLine);38 39 }
在這里用Where獲取了前1000條數(shù)據(jù),實際項目中基本不可能這樣來,或者全部ToList()出來,考慮到項目中有些情況下確實需要全部ToList()出來一些數(shù)據(jù),但是取1000條應該足夠了,對于其他情況下來講,這項測試沒有太大的意義,我們等會看分頁的性能。
附上一些全部ToList()出來時的測試:
當然實際是不可能這樣玩的,也就看看,看了一下內(nèi)存,3w多條數(shù)據(jù)也就30M左右。
附:Where查詢的一些優(yōu)化,其實這個之前是知道的,忘了往上貼了,謝謝@搵中求勝 博友的提醒,再次接著機會又測試了一下。
1.200w的數(shù)據(jù)(數(shù)據(jù)大才能體現(xiàn)出來效果),在沒有AsNoTracking的情況下
2.加上了AsNoTracking(),一般我們的查詢基本上不用跟蹤只要數(shù)據(jù)就行了。可以看出來性能明顯提高,同樣的數(shù)據(jù),將近提高了一般的性能。
1 var query = db.Users.AsNoTracking().Where(u => true);2 var queryWhere = query.Where(u =>u.UserType == type &&u.IsValid == isValid && u.Work == work && (u.Amount >= min && u.Amount <= max));
3.還有,許多情況下我們不需要全部的數(shù)據(jù),直接先用Select()選出來一些需要的字段,也會提高不少性能。
1 var query = db.Users.AsNoTracking().Where(u => true);2 var queryWhere = query.Where(u =>u.UserType == type &&u.IsValid == isValid && u.Work == work && (u.Amount >= min && u.Amount <= max))3 .Select(u=>new4 {5 u.Id,6 u.UserName7 });8 var list = queryWhere.ToList();
3.Any,F(xiàn)irst ,Count的測試
代碼都基本一樣,這里只附上一些圖片參考。
上邊的都能查詢存在不存在,但是相比來說,Any,First 對于存在的情況下,性能很好,而count對于不存在時性能卻很好,我也不知道為什么的。感覺有時候真的可以用Count查詢存在不存在的,畢竟平均效果好。PS:以前看一篇文章說Count比Any差了不知道多少倍,查詢存在不存在推薦用Any。現(xiàn)在看來,也差不多啊。
4.分頁查詢。
從實際項目來看,用戶在看分頁數(shù)據(jù)時,一般都是翻看前10頁左右,而且每頁的數(shù)據(jù)量也大概在10-30個之間,太多了沒必要。所有分頁的pageIndex和pageSize都設置在了這些數(shù)據(jù)之間,可能頁碼的大小pageIndex,pageSize過大的時候也會影響性能,這個我們隨后再加以測試。
200ms左右吧,基本還說的過去,可能是在排序的問題上花費了太多的時間。
附上一張pageIndex比較大的測試結(jié)果(pageIndex在800-1000之間),果然頁碼比較大的時候花費時間變長了,pageSize就不用說了,肯定時間也會變長。
5.Contains查詢
這里代碼稍微做了改動,感覺也跟這個沒關系
private void btnContains_Click(object sender, EventArgs e) { string[] usernames = new[] { "zhao", "wang", "li", "san", "zhaoliu" }; bool[] valids = new bool[] { false, true }; string[] works = new[] { "序猿", "攻城", "產(chǎn)品汪", "盤俠", "代碼" }; .... //全名稱改成了部分名稱,能保證是模糊查詢吧。。[笑] }
感覺確實有點慢,500ms左右,畢竟Contains,畢竟like,畢竟100w數(shù)據(jù)吧,有些條件下還是可以接受的,畢竟方便,做個自己用的查詢還是可以的。
六、數(shù)據(jù)量加大
既然是百萬級別,也不能只有一百萬。
1.二百萬的數(shù)據(jù)
總結(jié)一下:
Find無壓力,沒區(qū)別,大概是因為主鍵索引的緣故。
Any,First,Count都還在100ms左右,還能用。
分頁已經(jīng)到了400ms,感覺已經(jīng)不能接受了。但是我真的還沒咋見過能分幾千頁的,這里可以先用Where過濾到一些老舊數(shù)據(jù)或者不要的數(shù)據(jù)再進行分頁應該還是不錯的。
Contains已經(jīng)到了1s了,這對于用戶來說已經(jīng)不能接受了,但是到了這個級別的數(shù)據(jù),應該就用上檢索引擎了。這個就不考慮了。
2.三百萬的數(shù)據(jù)
總結(jié)一下:
Find無壓力,還是沒啥區(qū)別,大概是因為主鍵索引的緣故。
Any,First能查詢到結(jié)果時還是挺快了,Count感覺在這里更好用了。
分頁到了500ms,還是那句話,這里可以先用Where過濾到一些老舊數(shù)據(jù)或者不要的數(shù)據(jù)再進行分頁,可以看一下,分頁的總記錄數(shù)都是一,二百萬,算了自己想辦法優(yōu)化吧。
Contains不說了。
4.四百萬的數(shù)據(jù)
總結(jié)一下:
Find無壓力,還是沒啥區(qū)別,大概是因為主鍵索引的緣故。
Any,First查不到就慢了,Count感覺在這里更好用了。
分頁不說了。
Contains不說了。
七、結(jié)語
當寫到這里的時候,我感覺我錯了,這些好像和EF沒有半毛錢關系,這么簡單的查詢,EF生成Sql語句應該不耗費什么時間。根本沒有發(fā)揮出EF的linq語法什么的,各種復雜查詢語句,各種連接語句的生成。納尼?。。?/span>
但是既然都到這個地步了,那就算了,就當做是對Sql Server性能的考驗吧。話說應該200w數(shù)據(jù)的情況下,EF應該還是可以隨便這樣用的,再說了,我的用的是自己的個人電腦,要是用服務器肯定無壓力的。
感覺EF快不快還是和程序員寫的語句有關吧,怎么獲取數(shù)據(jù),怎么查詢,怎么拼接,畢竟到最后都是生成sql語句去查詢,所以瓶頸應該在如何快速的生成高效的Sql語句。
對于一個創(chuàng)業(yè)公司,剛開始做的項目,數(shù)據(jù)連幾十萬都不到,肯定果斷用EF啊,容易上手,開發(fā)方便,不用寫Sql是最重要的,畢竟微軟的東西,都迭代這么多版本了,應該優(yōu)化的差不多了吧。
PS:第一次寫博客,不知道測試的姿勢對不對,方向?qū)Σ粚?,有錯了大神指出來,請不要噴我,我會哭的[哈哈],我只是一個只會寫增刪改查的小碼農(nóng)。
http://www.cnblogs.com/flaming/p/7119544.html