一、起因 

   個人還是比較喜歡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的單表查詢功能,之后有時間會做復雜的鏈接查詢的測試。

三、測試條件

 iOS培訓,Swift培訓,蘋果開發(fā)培訓,移動開發(fā)培訓

      iOS培訓,Swift培訓,蘋果開發(fā)培訓,移動開發(fā)培訓

    老百姓的配置,自己的工作電腦。

    Sql Server 2012,Entity Framework 6.1.3。

四、測試數(shù)據(jù)

  鑒于以前看過的測試都是兩三個字段,且數(shù)據(jù)過于簡單,以防有這方面的影響,又因為實際項目中的字段可能較多,而且數(shù)據(jù)量也比較復雜,就模擬了一個較為接近的數(shù)據(jù)表,再說一遍,本著求真務實的革命主義方針,針對現(xiàn)實的項目來測試。

    iOS培訓,Swift培訓,蘋果開發(fā)培訓,移動開發(fā)培訓

    數(shù)據(jù)量100W

    iOS培訓,Swift培訓,蘋果開發(fā)培訓,移動開發(fā)培訓

五、開始測試

    做了一個WinForm的測試,界面如下

    iOS培訓,Swift培訓,蘋果開發(fā)培訓,移動開發(fā)培訓

    1.進行Find測試,隨機生成id,左邊顯示查詢用時,先上代碼。

    

iOS培訓,Swift培訓,蘋果開發(fā)培訓,移動開發(fā)培訓

 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         }

iOS培訓,Swift培訓,蘋果開發(fā)培訓,移動開發(fā)培訓

 

    結(jié)果如下:

    iOS培訓,Swift培訓,蘋果開發(fā)培訓,移動開發(fā)培訓

    可以看出,在100w數(shù)據(jù)的情況下,利用Find根據(jù)主鍵id查詢根本無壓力,至于第一次很長時間,應該是連接數(shù)據(jù)花費了一些時間。

   2.進行Where測試,代碼如下。

iOS培訓,Swift培訓,蘋果開發(fā)培訓,移動開發(fā)培訓

 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         }

iOS培訓,Swift培訓,蘋果開發(fā)培訓,移動開發(fā)培訓

 

  在這里用Where獲取了前1000條數(shù)據(jù),實際項目中基本不可能這樣來,或者全部ToList()出來,考慮到項目中有些情況下確實需要全部ToList()出來一些數(shù)據(jù),但是取1000條應該足夠了,對于其他情況下來講,這項測試沒有太大的意義,我們等會看分頁的性能。

  iOS培訓,Swift培訓,蘋果開發(fā)培訓,移動開發(fā)培訓

   附上一些全部ToList()出來時的測試:

  iOS培訓,Swift培訓,蘋果開發(fā)培訓,移動開發(fā)培訓

  當然實際是不可能這樣玩的,也就看看,看了一下內(nèi)存,3w多條數(shù)據(jù)也就30M左右。

  附:Where查詢的一些優(yōu)化,其實這個之前是知道的,忘了往上貼了,謝謝@搵中求勝 博友的提醒,再次接著機會又測試了一下。

  1.200w的數(shù)據(jù)(數(shù)據(jù)大才能體現(xiàn)出來效果),在沒有AsNoTracking的情況下

  iOS培訓,Swift培訓,蘋果開發(fā)培訓,移動開發(fā)培訓

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

 

 iOS培訓,Swift培訓,蘋果開發(fā)培訓,移動開發(fā)培訓

 3.還有,許多情況下我們不需要全部的數(shù)據(jù),直接先用Select()選出來一些需要的字段,也會提高不少性能。

iOS培訓,Swift培訓,蘋果開發(fā)培訓,移動開發(fā)培訓

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

iOS培訓,Swift培訓,蘋果開發(fā)培訓,移動開發(fā)培訓

 

 iOS培訓,Swift培訓,蘋果開發(fā)培訓,移動開發(fā)培訓

 3.Any,F(xiàn)irst ,Count的測試

  代碼都基本一樣,這里只附上一些圖片參考。

  iOS培訓,Swift培訓,蘋果開發(fā)培訓,移動開發(fā)培訓

  iOS培訓,Swift培訓,蘋果開發(fā)培訓,移動開發(fā)培訓

  iOS培訓,Swift培訓,蘋果開發(fā)培訓,移動開發(fā)培訓

  上邊的都能查詢存在不存在,但是相比來說,Any,First 對于存在的情況下,性能很好,而count對于不存在時性能卻很好,我也不知道為什么的。感覺有時候真的可以用Count查詢存在不存在的,畢竟平均效果好。PS:以前看一篇文章說Count比Any差了不知道多少倍,查詢存在不存在推薦用Any。現(xiàn)在看來,也差不多啊。

  4.分頁查詢。

  從實際項目來看,用戶在看分頁數(shù)據(jù)時,一般都是翻看前10頁左右,而且每頁的數(shù)據(jù)量也大概在10-30個之間,太多了沒必要。所有分頁的pageIndex和pageSize都設置在了這些數(shù)據(jù)之間,可能頁碼的大小pageIndex,pageSize過大的時候也會影響性能,這個我們隨后再加以測試。

  iOS培訓,Swift培訓,蘋果開發(fā)培訓,移動開發(fā)培訓

  200ms左右吧,基本還說的過去,可能是在排序的問題上花費了太多的時間。

  附上一張pageIndex比較大的測試結(jié)果(pageIndex在800-1000之間),果然頁碼比較大的時候花費時間變長了,pageSize就不用說了,肯定時間也會變長。

  iOS培訓,Swift培訓,蘋果開發(fā)培訓,移動開發(fā)培訓

  5.Contains查詢

   這里代碼稍微做了改動,感覺也跟這個沒關系 

iOS培訓,Swift培訓,蘋果開發(fā)培訓,移動開發(fā)培訓

 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)品汪", "盤俠", "代碼" };
        ....
    //全名稱改成了部分名稱,能保證是模糊查詢吧。。[笑]  
 }

iOS培訓,Swift培訓,蘋果開發(fā)培訓,移動開發(fā)培訓

 

 

 

    iOS培訓,Swift培訓,蘋果開發(fā)培訓,移動開發(fā)培訓

    感覺確實有點慢,500ms左右,畢竟Contains,畢竟like,畢竟100w數(shù)據(jù)吧,有些條件下還是可以接受的,畢竟方便,做個自己用的查詢還是可以的。

  六、數(shù)據(jù)量加大

  既然是百萬級別,也不能只有一百萬。

   1.二百萬的數(shù)據(jù)

   iOS培訓,Swift培訓,蘋果開發(fā)培訓,移動開發(fā)培訓

   iOS培訓,Swift培訓,蘋果開發(fā)培訓,移動開發(fā)培訓  iOS培訓,Swift培訓,蘋果開發(fā)培訓,移動開發(fā)培訓iOS培訓,Swift培訓,蘋果開發(fā)培訓,移動開發(fā)培訓

   iOS培訓,Swift培訓,蘋果開發(fā)培訓,移動開發(fā)培訓iOS培訓,Swift培訓,蘋果開發(fā)培訓,移動開發(fā)培訓iOS培訓,Swift培訓,蘋果開發(fā)培訓,移動開發(fā)培訓

   總結(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ù)

  iOS培訓,Swift培訓,蘋果開發(fā)培訓,移動開發(fā)培訓

  iOS培訓,Swift培訓,蘋果開發(fā)培訓,移動開發(fā)培訓iOS培訓,Swift培訓,蘋果開發(fā)培訓,移動開發(fā)培訓iOS培訓,Swift培訓,蘋果開發(fā)培訓,移動開發(fā)培訓

  iOS培訓,Swift培訓,蘋果開發(fā)培訓,移動開發(fā)培訓iOS培訓,Swift培訓,蘋果開發(fā)培訓,移動開發(fā)培訓iOS培訓,Swift培訓,蘋果開發(fā)培訓,移動開發(fā)培訓

  

  總結(jié)一下:

 

    Find無壓力,還是沒啥區(qū)別,大概是因為主鍵索引的緣故。

 

    Any,First能查詢到結(jié)果時還是挺快了,Count感覺在這里更好用了。

 

    分頁到了500ms,還是那句話,這里可以先用Where過濾到一些老舊數(shù)據(jù)或者不要的數(shù)據(jù)再進行分頁,可以看一下,分頁的總記錄數(shù)都是一,二百萬,算了自己想辦法優(yōu)化吧。

 

    Contains不說了。

 

   4.四百萬的數(shù)據(jù)

  iOS培訓,Swift培訓,蘋果開發(fā)培訓,移動開發(fā)培訓

  iOS培訓,Swift培訓,蘋果開發(fā)培訓,移動開發(fā)培訓iOS培訓,Swift培訓,蘋果開發(fā)培訓,移動開發(fā)培訓iOS培訓,Swift培訓,蘋果開發(fā)培訓,移動開發(fā)培訓

  iOS培訓,Swift培訓,蘋果開發(fā)培訓,移動開發(fā)培訓iOS培訓,Swift培訓,蘋果開發(fā)培訓,移動開發(fā)培訓iOS培訓,Swift培訓,蘋果開發(fā)培訓,移動開發(fā)培訓

  

 總結(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