EF查詢百萬級數據的性能測試--單表查詢


一、起因 

個人還是比較喜歡EF的,畢竟不用寫Sql,開發效率高,操作簡單,不過總是聽人說EF的性能不是很好,也看過別人做的測試,但是看了就以為真的是那樣。但是實際上到底是怎么樣,說實話我真的不知道。我只知道選什么的框架是基於實際情況的,博主在一個創業公司上班,選的就是EF框架,剛做了一個項目,數據也就幾萬不到,感覺性能沒那么差勁。於是,就想多弄點數據測試一下。再說一遍,本着 求真務實的方針,是針對現實中的業務需求來測試的,不是來單比性能的。你要是做個ERP系統,都去考慮千萬級並發的架構,那當我沒說。畢竟不是基於實際項目的框架選擇都是耍流氓。

二、聲明

基於實際的項目,考慮到博主一般的遇到的上線項目對於數據的增刪改操作時,操作的數據一般都是一個,兩個,多了有十幾個,對於一下同時提交幾十個數據進行增刪改的,原諒博主還沒有見過,更有甚者,提交幾百個數據進行增刪改,博主想也是沒有想過。但是在這個數量級下的增刪改操作,我相信EF還是能夠勝任的,所以本文不再測試EF的增刪改性能,因為感覺完全能夠滿足一般項目的需要。本文只測試EF的單表查詢功能,之后有時間會做復雜的鏈接查詢的測試。

三、測試條件

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

Sql Server 2012,Entity Framework 6.1.3。

四、測試數據

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

數據量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 = "執行" + count + "次,平均耗時" + new TimeSpan((ts.Ticks / count));
22         }

結果如下:

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

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[] { "程序猿", "攻城獅", "產品汪", "鍵盤俠", "代碼狗" };
 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()+"條數據,用時:" + time + Environment.NewLine);
38 
39         }

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

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

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

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

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

2.加上了AsNoTracking(),一般我們的查詢基本上不用跟蹤只要數據就行了。可以看出來性能明顯提高,同樣的數據,將近提高了一般的性能。

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()選出來一些需要的字段,也會提高不少性能。

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=>new
4                 {
5                     u.Id,
6                     u.UserName
7                 });
8 var list = queryWhere.ToList();

3.Any,First ,Count的測試

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

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

4.分頁查詢。

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

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

附上一張pageIndex比較大的測試結果(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[] { "序猿", "攻城", "產品汪", "盤俠", "代碼" };
        ....
    //全名稱改成了部分名稱,能保證是模糊查詢吧。。[笑]  
 }

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

六、數據量加大

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

1.二百萬的數據

  

   

總結一下:

Find無壓力,沒區別,大概是因為主鍵索引的緣故。

Any,First,Count都還在100ms左右,還能用。

分頁已經到了400ms,感覺已經不能接受了。但是我真的還沒咋見過能分幾千頁的,這里可以先用Where過濾到一些老舊數據或者不要的數據再進行分頁應該還是不錯的。

Contains已經到了1s了,這對於用戶來說已經不能接受了,但是到了這個級別的數據,應該就用上檢索引擎了。這個就不考慮了。

2.三百萬的數據

  

總結一下:

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

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

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

Contains不說了。

4.四百萬的數據

總結一下:

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

Any,First查不到就慢了,Count感覺在這里更好用了。

分頁不說了。

Contains不說了。

七、結語

當寫到這里的時候,我感覺我錯了,這些好像和EF沒有半毛錢關系,這么簡單的查詢,EF生成Sql語句應該不耗費什么時間。根本沒有發揮出EF的linq語法什么的,各種復雜查詢語句,各種連接語句的生成。納尼!!!

但是既然都到這個地步了,那就算了,就當做是對Sql Server性能的考驗吧。話說應該200w數據的情況下,EF應該還是可以隨便這樣用的,再說了,我的用的是自己的個人電腦,要是用服務器肯定無壓力的。

感覺EF快不快還是和程序員寫的語句有關吧,怎么獲取數據,怎么查詢,怎么拼接,畢竟到最后都是生成sql語句去查詢,所以瓶頸應該在如何快速的生成高效的Sql語句。

對於一個創業公司,剛開始做的項目,數據連幾十萬都不到,肯定果斷用EF啊,容易上手,開發方便,不用寫Sql是最重要的,畢竟微軟的東西,都迭代這么多版本了,應該優化的差不多了吧。

PS:第一次寫博客,不知道測試的姿勢對不對,方向對不對,有錯了大神指出來,請不要噴我,我會哭的[哈哈],我只是一個只會寫增刪改查的小碼農。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM