最近項目有點閑,終於可以了解點自己想了解的了,以前聽同事講面試的經歷總會被問到“如何處理高並發大數據” 乍一聽感覺這東西好像很有學問的樣子,於是並發這個詞在腦海里留深刻印像,而且在自己心中的技術地位也提高很多,也導致了解並發相關的知識時,也帶着思想負擔,總以為很難懂,程序員或許都是這樣,在自己不懂的技術領域,別人說一個很簡單的技術,給他的感覺都是很高深的樣子,其實自己一了解就會發現,“哎喲 我 C 原來就這樣兒啊!”總之只要你想了解,花點時間就沒有難的事! 好了 正式進入正題
EF並發處理 在此之前先把與並發相關的一此知識點給大家介紹一下,有些雖然不是了解並發的必要條件,但我認為多了解一點又有什么不好呢?
一 EF並發介紹
什么叫並發:當多個用戶同時更新同一數據的時候,由於更新可能導致數據的不一致性,使得程序的業務數據發生錯誤,這種情況可以稱之為並發。
並發又分為兩種:樂觀並發 與 悲觀並發
樂觀並發:即系統允許多個用戶同時修改同一條記錄,系統會預先定義由數據並發所引起的並發異常處理模式,去處理修改后可能發生的沖突
當出現樂觀並發時應該怎么處理呢,通常有如下三種處理方法
a 保留最后一次對象修改的值
b 保留最初的修改值
c 合並修改值
這三種處理方法下文會一一介紹
悲觀並發:在同一時刻只允許一個用戶修改相同數據,直接用Lock 與 unLock就可以處理,后文就不再解釋了
二 EF對象狀態
在EF中所有的對象狀態只有被添加到ObjectContext 上下文中才能被跟蹤,才能進行持久化操作,那么在ObjectContext中對於對象狀態分幾種呢?有如下五種
三 EF事務隔離級別
在這里只例舉三個最常用到的隔離級別,其它的有待朋友們自己行研究了
ReadCommitted:不可以在事務期間讀取可變數據,但是可以修改它(EF 默認的隔級別)
ReadUncommitted:可以在事務期間讀取和修改可變數據。
Serializable:可以在事務期間讀取可變數據,但是不可以修改,也不可以添加任何新數據。
隨着隔離級別的提高,可以更有效地防止數據的不一致性。但是,這將降低事務的並發處理能力,會影響多用戶訪問
四 事務不同隔離級別帶來數據讀取的不同結果
臟讀:當一個事務讀取數據修改后以經SaveChange但事務還沒有提交,此時另外一個事務就讀取了該數據,此時的數據就是臟數據,這一過程就是臟讀
不可重復讀:是指在一個事務內,多次讀同一數據。在這個事務還沒有結束時,另外一個事務也訪問該同一數據。那么,在第一個事務中的兩次讀數據之間,由於第二個事務的修改,那么第一個事務兩次讀到的的數據可能是不一樣的。這樣就發生了在一個事務內兩次讀到的數據是不一樣的這一過程就是不可重復讀
幻讀:一個事務針對一張表的所有數據進行讀取修改,而此時另一個事務向表中插入了一條數據,則第一個事務數據不包含新數據,像出現幻覺一樣,這一過程就是幻讀
ReadCommitted 會引發 不可重復讀 和幻讀
ReadUncommitted 會引發 臟讀 ,不可重復讀 和幻讀
Serializable 以上三種都不會發生
可見 Serializable的隔離級別是最高的,數據也是最准確的,但是高正確率也是要付出代價的,在此種隔離級別下,讀取數據,與更新數據的效率也是最低的
五 EF並發的處理流程
對於並發原理是這樣的,每一次操作表 字段TimeStamp的值都會改變,而每次對表做操作時都會比對對象的TimeStamp值與數據庫中表的值是否相同,是則操作表,否 ,則拋出異常給客戶端或是刷新對象狀態后重新保存,詳細步驟如下
1 在表中新建一個字段類型為TimeStamp , TimeStamp類型在.netFrameWork中是一個8位的數組,在SQL中則是一串二進制字符
2 在.edmx 對象實體模型圖中,右鍵TimeStamp字段 屬性-->並發模式-->選擇Fixed
注意:把並發模式 設為Fixed后每一次操作表都會把TimeStamp字段當做條件查詢,只有相等才能成功,以下是用SQL Profile跟蹤到的結果 在 where 處可以看到效果
3 模擬並發,通過捕獲異常在異常處返回消息給客戶端 異常類型有如下兩種
1 //第一次加載對象更新后暫不保存到數據庫 2 var fContext = new BolgModelEntities(); 3 var menuObj = fContext.Menu.FirstOrDefault(); 4 menuObj.MenuName = "C#"; 5 6 using (var sContext = new BolgModelEntities()) 7 { 8 //第二次加載對象更新后,保存到數據庫 此時TimeStamp的值已改變,與menuObj對象的TimeStamp值已不同,所以在menuObj保存時會拋異常出來 9 var obj = sContext.Menu.FirstOrDefault(); 10 obj.MenuName = "WPF"; 11 sContext.SaveChanges();//可以順利保存 12 } 13 14 try 15 { 16 //保存會拋異常,因為TimeStamp 值不匹配 17 fContext.SaveChanges(); 18 } 19 //catch (DbUpdateConcurrencyException ex) //EF 自定義異常 20 //{ 21 22 // fContext.Refresh(RefreshMode.ClientWins, menuObj); 23 // fContext.SaveChanges(); 24 //} 25 catch (System.Data.OptimisticConcurrencyException ex) //.net FreamWork 定義異常 26 { 27 //捕獲異常后依然對數據進行保存 28 fContext.Refresh(RefreshMode.ClientWins, menuObj); // RefreshMode.ClientWins 保存對象更新后的值 且對象的狀態為Modified。 29 30 fContext.Refresh(RefreshMode.StoreWins, menuObj);//RefreshMode.StoreWins 保存數據庫中原有值, 且對象的狀態為Modified 31 32 fContext.SaveChanges();//SaveChanges完成后對象狀態變為Unchanged 33 34 } 35 catch (System.Data.OptimisticConcurrencyException ex) 36 { 37 //捕獲異常后不再處理,將消息返回給客戶端 38 string message = string.Empty; 39 message = "出現數據沖突請重新提交"; 40 return message; 41 }
在並發拋出異常后可以根據業務需,向客戶端返回消息,
也可以直接處理沖突后的數據
a 保留最后一次對象修改的值 用 RefreshMode.ClientWins
b 保留最初的修改值 用 RefreshMode.StoreWins
c 合並修改值 針對同對象不同屬性一樣可以 用 RefreshMode.StoreWins
好了到此 關於EF的並發就寫完了,當然我只是把並發的基礎說了一下,對於更高效,科學的並發,還需要朋友根據自己項目 的情況來做相應的處理
其實 寫完了才發現關於EF的並發 其實並沒有想象中的那么麻煩,只要多花點時間,多看看資料,問題就不大了
對於以上內容,如有不對之處,還望各位能指出,不要讓我誤導了他人,
另外如果覺得,本文對你有那么一點幫助,還望不吝嗇的點一點 推薦,您的推薦將是我源源不斷的寫作力!