ado.net與EF的關係之EF生成SQL的效率研究

EF和SqlHelper 簡單三層

  •  EF生成sql,再調用ado.net訪問數據庫,最後使結果對象具體化.
  •  以前的SqlHelper 簡單三層的寫法,拼接sql語句字符串,再調用ado.net訪問數據庫,最後也是把結果轉換爲對象.

明顯的區別:sql語句的產生,EF是SQL查詢命令和 LINQ 查詢生成,SqlHelper簡單三層是程序員直接拼接sql語句.
那麼,一直談EF性能,不得不說EF產生sql的速度.html

回顧上篇文章提到的 EntityFramework性能之預生成視圖 ,裏面有張 查詢執行的各個階段 圖解.
各個階段以下:java

  1. 加載元數據,中等成本,在每一個應用程序域中一次.
  2. 打開數據庫鏈接.(使用ado.net也免不了.)
  3. 生成視圖,成本雖高,卻在每一個應用程序域中指執行一次.(上篇文章折騰了半天,最終以爲EF6.1.3和.net 4.5已經優化過了,沒必要在乎這個預生成視圖.)
  4. 準備查詢,中等成本.每一個惟一查詢一次。註釋:由於實體 SQL查詢命令和 LINQ 查詢現已緩存,因此,之後執行相同查詢所需的時間較少。 您仍可使用已編譯的 LINQ 查詢來下降後續執行中的這一開銷,編譯的查詢比自動緩存的 LINQ 查詢效率更高。
  5. 執行查詢,低成本,每一個查詢一次。註釋:使用 ADO.NET 數據提供程序對數據源執行命令的成本。 由於大多數數據源緩存查詢計劃,因此,之後執行相同查詢所需的時間可能較少。
  6. 加載和驗證類型,跟蹤,使對象具體化

創建項目

(EF中仍是用以前的PhoneBookModel.edmx,熟悉的名字.)
此次用asp.net mvc.在Home控制器下,有兩個方法.程序員

public ActionResult PreHot()
        {
            var db = new PhoneBookEntities();
            db.ContactInGroup.ToList();
            return View();
        }
PreHot
public ActionResult Test()
        {
            Stopwatch sw = new Stopwatch();
            sw.Start();

            using (var db = new PhoneBookEntities())
            {
                var gi = db.GroupInfo.FirstOrDefault(c => c.GroupName.Contains("g1!"));
                var ci = db.ContactInfo.FirstOrDefault(c => c.ID == 12);
                ci.ContactName += "!";
                gi.GroupName += "!";

                using (var tx = db.Database.BeginTransaction())
                {
                    try
                    {
                        db.Database.ExecuteSqlCommand("update GroupInfo set GroupName='hello' where GroupId=209");
                        db.SaveChanges();
                        tx.Commit();//此語句不要漏了,不然監控結果會是釋放了事務,而不是提交了事務!
                    }
                    catch (Exception)
                    {
                        tx.Rollback();
                    }
                }
            }
            sw.Stop();
            ViewBag.time = sw.ElapsedMilliseconds;//在視圖裏顯示花費的時間
            return View();
        }
Test

注意:以前文章提過,怎麼監控sql語句.而在監控記錄裏,會給出執行sql語句的時間.對於Test方法中EF生成的sql語句,
記錄顯示:    執行-- 已在 1 毫秒內完成,結果爲: SqlDataReader. web

測試開始:

  • a操做.清理解決方案,生成,先訪問/home/index,再直接訪問 /Home/Test,用時1250毫秒,再次訪問 /Home/Test ,時間顯示2-15毫秒.(/home/index,裏面沒任何代碼,僅用來啓動網站)
  • b操做.清理解決方案,生成,先訪問/home/index,以後訪問/Home/PreHot,再訪問 /Home/Test,顯示時間 250毫秒,再次訪問 /Home/Test 時間顯示2-15毫秒.

對於這個結果,個人解釋是:
a操做,訪問/Home/Test,執行Test()方法,而此時,要走 查詢執行階段的1,2,3,4,5,6,對比以前的 查詢執行的各個階段,就明白爲何會用時1250毫秒這麼久(相比數據庫執行查詢只須要1毫秒.);sql

再次訪問/Home/Test時, 查詢執行階段的1和3不用走(應用程序域中一次),4也不走(每一個惟一查詢一次[Test()方法裏的查詢執行過一次了,不惟一了,查詢已自動緩存]),5執行查詢(使用了緩存查詢計劃,使之後執行相同查詢所需的時間可能較少),再走6.因此用時大幅度下降到2-15毫秒(顯示時間大多在5毫秒左右,給個小公式:5毫秒=執行sql語句用時1毫秒+EF產生sql語句用時4毫秒).數據庫

b操做,訪問/Home/PreHot,先讓 查詢執行階段的1和3走了一次.再訪問 /Home/Test,走4,5,6,顯示時間 250毫秒(對比a操做首次訪問 /Home/Test,少了對性能影響較大的1和3階段).c#

再次訪問 /Home/Test 時間顯示2-15毫秒(同a操做).緩存

也就是說,通過EF的初次使用,再加上 實體 SQL查詢命令和 LINQ 查詢 被自動緩存,知足這兩個條件後,生成sql語句速度極快(應該不會大於5毫秒).
若是一個網站使用的是EF,稍微有點訪問量,EF生成sql語句的速度已不是問題.(一樣的測試Test()方法,在控制檯裏,時間一直是350毫秒左右...?) 服務器

生成sql語句的速度已不是問題,那麼接下來就是生成sql語句的質量.以前的博文,但願你們看過.
若是EF生成sql的質量夠好,EF的效率就不是問題,我感受其效率會無限接近原生的ado.net.
下篇,會拿 SqlHelper操做Ado.net跟使用EF作個對比.mvc

若是,你初學EF,有一個表ContactInfo,含100W行數據, 而你寫了一行這樣的代碼:
var res= db.ContactInfo.ToList().Skip(5).Take(5);
發現速度極慢,你就想EF真垃圾,我不使用EF,毫秒中搞定.不再看EF啦.那就是你的損失了.自已用的很差,卻在怪EF不行.

Ado.net和EF的關係,就比如c跟java和c#的關係.
理論上說,c的效率要比java和c#高.但java和c#仍是被普遍使用.
可是,一樣的功能,一樣用c#去寫,有的人寫的程序會高效.由於他在用c#語言中,會避開耗性能的拆箱裝箱,會更高效地去使用StringBuilder去拼接比較複雜的字符等.
c#語言自己的好的.可是程序員能不能高效地使用它又是另一回事了.

一樣的,EF也是好的,就看你對它的瞭解有多深刻了.
會Ado.net,知道怎麼寫sql更優,明白怎麼監控EF產生的sql語句,就會對瓶頸進行調優,就能把EF變成本身的利器.這也就是我先看EF產生的sql的緣由.
若是什麼都不知道,就用EF一頓瞎寫,就比如用webForm一頓拖服務器控件, 把網址拖慢了,把人拖的腦殘了(只會拖控件,換個jsp就懵逼了...估計控件拖的連請求,處理,響應都弄不明白.)
(博客園 dudu 關於EF的文章,我大多看了,不過版本是EF4.根據 dudu博文,博客園也是用了EF這個利器的.)

您認爲EF如何呢? 

相關文章
相關標籤/搜索