EntityFramework Core不得不注意的性能优化意外收获,你会用错?
前言
這兩天在著實(shí)研究EF Core項(xiàng)目當(dāng)中對(duì)于一些查詢也沒(méi)實(shí)際去檢測(cè),于是想著利用放假時(shí)間去實(shí)際測(cè)試下,結(jié)果本文就出來(lái)了,too young,too simple,后續(xù)博主會(huì)從底層翻譯表達(dá)式樹弄起,來(lái)從源頭了解EF Core,通過(guò)本文你會(huì)明白不是EF Core團(tuán)隊(duì)沒(méi)做性能優(yōu)化,而是你根本就沒(méi)用過(guò)而且正在倒退。
EntityFramework Core性能優(yōu)化初探
簡(jiǎn)單粗暴直接上代碼,給出上下文以及需要用到的測(cè)試類,如下:
public class EFCoreContext : DbContext
? ? {
? ? ? ? public DbSet<Blog> Blogs { get; set; }
? ? ? ? protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
? ? ? ? ? ? => optionsBuilder.UseSqlServer(@"Server=.;Database=EFCoreDb;Trusted_Connection=True;");
? ? ? ? protected override void OnModelCreating(ModelBuilder modelBuilder)
? ? ? ? {
? ? ? ? ? ? modelBuilder.Entity<Blog>(pc =>?
? ? ? ? ? ? {
? ? ? ? ? ? ? ? pc.ToTable("Blog").HasKey(k => k.Id);
? ? ? ? ? ? ? ? pc.Property(p => p.Name).IsRequired();
? ? ? ? ? ? ? ? pc.Property(p => p.Url).IsRequired();
? ? ? ? ? ? ? ? pc.Property(p => p.Count).IsRequired();
? ? ? ? ? ? ? ? pc.Property(p => p.RowVersion).IsRequired().IsRowVersion().ValueGeneratedOnAddOrUpdate();
? ? ? ? ? ? });
? ? ? ? }
? ? }
你是否像如下去獲取分頁(yè)數(shù)據(jù)呢,我們來(lái)一起瞧瞧:
var ef = new EFCoreContext();
? ? ? ? ? ? var blogs = ef.Blogs;
? ? ? ? ? ? var example1 = blogs
? ? ? ? ? ? ? ? .Skip(1)
? ? ? ? ? ? ? ? .Take(1)
? ? ? ? ? ? ? ? .ToList();
? ? ? ? ? ? var example2 = blogs
? ? ? ? ? ? ? ? .Skip(10)
? ? ? ? ? ? ? ? .Take(10)
? ? ? ? ? ? ? ? .ToList();
我們通過(guò)如下SQL語(yǔ)句來(lái)查看查詢計(jì)劃生成的SQL語(yǔ)句:
SELECT?
? ? sys.syscacheobjects.cacheobjtype,
? ? sys.dm_exec_query_stats.execution_count,
? ? sys.syscacheobjects.SQL,
? ? sys.dm_exec_query_plan.query_plan
FROM sys.dm_exec_query_stats
? ? INNER JOIN sys.dm_exec_cached_plans
? ? ?ON sys.dm_exec_cached_plans.plan_handle = sys.dm_exec_query_stats.plan_handle
? ? INNER JOIN sys.syscacheobjects ON sys.syscacheobjects.bucketid = sys.dm_exec_cached_plans.bucketid
CROSS APPLY sys.dm_exec_query_plan(sys.dm_exec_query_stats.plan_handle)
結(jié)果如下:
我們?cè)賮?lái)看看xml文件中生成的SQL語(yǔ)句是怎樣的。
這說(shuō)明什么問(wèn)題呢,上述查詢計(jì)劃中生成的SQL語(yǔ)句對(duì)于我們上述去取數(shù)據(jù)首選從第二條取一條,接下來(lái)是去取第十條后的十條,同時(shí)上述SQL語(yǔ)句而是聲明了兩個(gè)變量,換言之,上述兩條語(yǔ)句查詢最終在第一次查詢后SQL查詢計(jì)劃進(jìn)行了緩存,下次再去取數(shù)據(jù)時(shí)直接調(diào)用此SQL語(yǔ)句以此達(dá)到重用的目的,下面要是我們進(jìn)行如下改造,結(jié)果會(huì)怎樣呢?
var ef = new EFCoreContext();var blogs = ef.Blogs;
var count = 1;
var example1 = blogs.Skip(count).Take(count).ToList();count = 10;
var example2 = blogs.Skip(count).Take(count).ToList();
結(jié)果經(jīng)過(guò)上述改造利用變量的形式和直接賦值的形式是一致的,沒(méi)有什么可講的,下面我們?cè)賮?lái)講述另外一種情況。請(qǐng)繼續(xù)往下看。
var ef = new EFCoreContext();var blogs = ef.Blogs;
var skipTakeWithInt1 = blogs.OrderBy(b => b.Id).Where(d => d.Name.Length > 1).ToList();
var skipTakeWithInt2 = blogs.OrderBy(b => b.Id).Where(d => d.Name.Length > 10).ToList();
看出什么沒(méi)有,對(duì)于上述兩條查詢則是對(duì)應(yīng)進(jìn)行了兩次SQL查詢,這下意識(shí)到了其中玄機(jī)了吧,下面我們將上述再改造一下:
var ef = new EFCoreContext();var blogs = ef.Blogs;
var length = 1;
var skipTakeWithVariable1 = blogs.OrderBy(b => b.Id).Where(d => d.Name.Length > length).ToList();length = 10;
var skipTakeWithVariable2 = blogs.OrderBy(b => b.Id).Where(d => d.Name.Length > length).ToList();
我們利用變量替換值改造后結(jié)果生成的SQL語(yǔ)句與上述截然不同,這里同樣是聲明了長(zhǎng)度的變量,你發(fā)現(xiàn)沒(méi)該變量居然和我們聲明的變量長(zhǎng)度是一致的,有點(diǎn)神奇,如此這樣通過(guò)變量替換值得方式來(lái)達(dá)到SQL查詢計(jì)劃重用的目的,說(shuō)到這里,我們是不是應(yīng)該回答一下為什么會(huì)有這樣的情況發(fā)生呢。很多東西當(dāng)你一直沒(méi)用到時(shí)就覺(jué)得不會(huì)用到壓根不用學(xué),其實(shí)不然,比如這樣,其本質(zhì)到底是怎樣的呢,其實(shí)是因?yàn)榍懊嫖乙呀?jīng)講過(guò)【閉包】的原因。
?
lambda表達(dá)式對(duì)我們聲明的變量進(jìn)行了捕獲然后延長(zhǎng)了其生命周期,也就是說(shuō)將變量類似變成類中一個(gè)字段了,類似如下:
[CompilerGenerated]public sealed class ExampleClass1{
public int Length;}[CompilerGenerated]
public sealed class ExampleClass2{
public int Length;}
自動(dòng)編譯生成兩個(gè)類同時(shí)存在兩個(gè)對(duì)于長(zhǎng)度的字段。接下來(lái)當(dāng)我們利用變量進(jìn)行查詢就演變了如下這樣:
var ef = new EFCoreContext();var blogs = ef.Blogs;
var length = 1;
var example1 = new ExampleClass1() { Length = length }; ?
? ? ? ? ?var skipTakeWithVariable1 = blogs.OrderBy(b => b.Id).Where(d => d.Name.Length > example1.Length).ToList();length = 10;
var example2 = new ExampleClass2() { Length = length }; ? ? ?
? ? ?var skipTakeWithVariable2 = blogs.OrderBy(b => b.Id).Where(d => d.Name.Length > example2.Length).ToList();
這樣就很明了了為什么通過(guò)變量達(dá)到查詢計(jì)劃重用的目的。
總結(jié)
關(guān)于EntityFramework Core雖然目前設(shè)計(jì)的性能非常好,但是有些東西等我們?nèi)ビ脮r(shí)還得多加驗(yàn)證,看其背后的本質(zhì)是怎樣的,才能不會(huì)心生疑竇,到底該怎樣用,如何用,心中要有定數(shù)才是,一點(diǎn)一滴的細(xì)小優(yōu)化不注意最終將導(dǎo)致大意失荊州。接下來(lái)博主會(huì)在三個(gè)方向不定時(shí)更新博客,第一個(gè)是SQL Server性能優(yōu)化系列,第二個(gè)是ASP.NET Core MVC/WebAPi,第三個(gè)則是EntityFramework Core原理解析,敬請(qǐng)期待。
原文地址:http://www.cnblogs.com/CreateMyself/p/6665423.html
.NET社區(qū)新聞,深度好文,微信中搜索dotNET跨平臺(tái)或掃描二維碼關(guān)注
總結(jié)
以上是生活随笔為你收集整理的EntityFramework Core不得不注意的性能优化意外收获,你会用错?的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 在ASP.NET Core中使用Apwo
- 下一篇: [认证授权] 2.OAuth2授权(续)