原文:https://www.cnblogs.com/wangyuliang/p/10338902.html
https://www.cnblogs.com/simadi/p/6879366.html
現在工作中很少使用原生的sql了,大多數的時候都在使用EF。剛開始的時候,只是在注重功能的實現,最近一段時間在做服務端接口開發。開發的時候也是像之前一樣,鍵盤噼里啪啦的一頓敲,接口秒秒鍾上線,但是到聯調測試的時候就悲劇了。。。。那叫一個慢啊,客戶端有種“千年等一回的趕腳” 。由於訪問量和數量都提升了一個數量級,之前沒有 考慮過的問題,都在此時暴露了,根據自己百度、google的經歷實踐,整理了這一些優化點。歡迎各位大神批評指正!
1.使用AsNoTracking(),無跟蹤查詢,查詢出的數據不可以修改,但是可以提高查詢速度
對於只讀操作,強烈建議使用AsNoTracking進行數據獲取,這樣省去了訪問EF Context的時間,會大大降低數據獲取所需的時間。
同時由於沒有受到上下文的跟蹤緩存,因此取得的數據也是及時最新的,更利於某些對數據及時性要求高的數據查詢。
db.Person.Where(a => a.IsDeleted == false).AsNoTracking().ToList();
2.合理使用延遲加載。
如果用不到導航屬性中的數據,那么使用懶加載就行了,不會加載不需要的數據到內存中。但是,如果會在 foreach 中使用導航屬性中的數據,那么最好是禁用懶加載,通過Include()方法,一次加載全部數據,防止在 foreach 多次和數據庫進行交互。當然,一般情況下是這樣的,具體的還是要根據當時的業務情況而定。
3.判斷List中是否含有數據的時候,最好使用Any(),避免使用Count()>0,這么Low的方式,真是慢的一逼啊。
4.在where子句中進行中文字符模糊匹配值的時候,記得加上使用DbFunctions.AsNonUnicode("要匹配的字符")。
var query = db.User.Where(a=>a.Name=="makmong").ToList();
修改后:var query = db.User.Where(a=>a.Name== DbFunctions.AsNonUnicode("makmong")).ToList();
5.錯誤的使用OrderBy() 導致的錯誤排序。
要對多個字段進行先后組合排序的時候,正確的是Orderby().ThenBy();切記不要這樣啊:OrderBy().OrderBy(),這樣達不到你想要的效果,只會按照最后的那個排序字段進行排序。
6.真假分頁的問題
line1:query.ToList().Skip((PageIndex - 1) * PageSize).Take(PageSize); line2: query.Skip((PageIndex - 1) * PageSize).Take(PageSize).ToList();
line1就是加分頁,直接從DB中取出全部的數據,然后在內存中進行分頁;line2才是真正分頁。沒有ToList()的時候,返回值類型還是IQueryable<T> ,執行了ToList(),之后直接查詢數據庫了,返回值類型直接是IEnumerable<T>。
7.按需加載部分列。
建議使用ViewModel代替實體Model
接着新建ViewModel
public class UserViewModel { public int Id { get; set; } public string Name { get; set; } }
開始查詢:
var query = db.User.Select(a=>new UserViewModel() { Id = a.Id, Name = a.Name }).ToList();
Model實體中枚舉使用byte類型
我們先來了解下Sqlserver中tinyint, smallint, int, bigint的區別
-
bigint:從-263(-9223372036854775808)到263-1(9223372036854775807)的整型數據,存儲大小為 8 個字節。一個字節就是8位,那么bigint就有64位
-
int:從-231(-2,147,483,648)到231-1(2,147,483,647)的整型數據,存儲大小為 4 個字節。int類型,最大可以存儲32位的數據
-
smallint:從-215(-32,768)到215-1(32,767)的整數數據,存儲大小為 2 個字節。smallint就是有16位
tinyint:從0到255的整數數據,存儲大小為 1 字節。tinyint就有8位。
所以對於有些范圍比較短的數值長度,例如枚舉類型值,完全可以使用byte類型替換int類型,對應生成數據庫tinyint類型以節省數據存儲。
如:
public CouponType CouponType { get; set; } public enum CouponType : byte { RedBag = 0, Experience = 1, Cash = 2, JiaXiQuan = 3 }
8.使用擴展庫Entity Framework Extendeds 進行批量Insert和delete操作,避免生成大量的sql語句和數據庫進行多次交互。
Nuget****安裝 PM> Install-Package EntityFramework.Extended
9.這些做了,性能還是不行?那SqlQuery(),或者通過儲存過程,將多條sql語句作為一個提交單元,也可以減少與數據庫的交互次數,從而提高性能。
對於EF首次啟動慢的問題,可通過下面的措施進行優化:
1.EF的預熱問題,在應用程序進行初始化的時候就事先進行預熱。比如,mvc應用程序可在global文件中添加如下操作。
protected void Application_Start() { using (var ctx = new mcccEntities()) { var objectContext = ((IObjectContextAdapter)ctx).ObjectContext; var mappingCollection = (StorageMappingItemCollection)objectContext.MetadataWorkspace.GetItemCollection(DataSpace.CSSpace); mappingCollection.GenerateViews(new List<EdmSchemaError>()); } //記得,連接了幾個DB就要寫幾個using啦。 }
EF使用SQL分庫操作
當數據庫的表及數據達到一定規模后我們想到的優化就有分庫,分表之類的優化操作。
對於之前的ADO.NET來說分庫是一件很普通的操作。
比如下面的非跨數據庫查詢語句:
SELECT Name FROM dbo.User WHERE ID=1
跨數據庫查詢語句:
SELECT Name FROM MaiMangAdb.dbo.blog_PostBody WHERE ID=1
我們知道EF的DbContext中已經指定了連接字符串
public EntityDB() : base("DefaultConnection")
<connectionStrings> <add name="DefaultConnection" connectionString="Data Source=.;Initial Catalog=EFStudy;Integrated Security=True;" providerName="System.Data.SqlClient" /> </connectionStrings>
也就是說所有的上下文操作都是基於這個數據庫來操作的,那我們就不能用ADO.NET那套,多個查詢配多個鏈接去操作數據庫。
當然大神們也給出了一套方法,而且也是簡單明了。那我也就直接將其移植過來記錄一下吧。
方法就是給數據庫添加SYNONYM 同義詞,我在此演示下
創建2張Model表User和Role
public class User { [Key] public int Id { get; set; } public string Name { get; set; } public bool IsDeleted { get; set; } [DateTime2Precision] public DateTime CreateDateTime { get; set; } } public class Role { [Key] public int Id { get; set; } public string Name { get; set; } }
並添加一條語句:
EntityDB db = new EntityDB(); db.User.Add(new User { Id = 1, Name = "ddd" ,CreateDateTime = DateTime.Now}); db.Role.Add(new Role() {Id = 1, Name = "admin"}); db.SaveChanges();
運行查看數據庫:
現在數據庫表及內容都有了。然后我要把User表及內容移植到另一個數據庫中,且不影響當前的EF操作。
創建新的數據庫EFSYNONYM並添加User表,表結構和EFStudy中的User一致。
然后在EFStudy中刪除表User且創建同義詞
CREATE SYNONYM [dbo].[Users] FOR [EFSYNONYM].[dbo].[Users]
此時的User和Role已經分別存在於不同的數據庫里面,我們來插入查詢數據操作下
至此分庫成功。當然此方法也有個缺點就是分庫表和主表間由同義詞關聯而無法建立主外鍵關系(其實當數據量達到一定級別后聯合join查詢反而不如分開多次查詢來得快,
且由於在同一個上下文中,不用太過於關心由數據多次連接開關而產生影響,凡事有利弊總得有個最優是吧),因此我們可以把一些獨立的容易過期的數據表給移植到單獨的數據庫,利於管理同時也利於優化查詢。