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


EF和SqlHelper 簡單三層

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

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

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

  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. 

測試開始:

  • 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毫秒.);

再次訪問/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階段).

再次訪問 /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做個對比.

如果,你初學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如何呢? 


免責聲明!

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



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