悲觀鎖:相信並發是絕大部分的,每次去拿數據的時候都認為別人會修改,所以每次在拿數據的時候都會上鎖,這樣別人想拿這個數據就會阻塞等待直到它拿到鎖,並且每一個線程都必須要達到目的的。
樂觀鎖:相信並發是極少數的,所以不會上鎖,但是在更新的時候會判斷一下在此期間別人有沒有去更新這個數據,可以使用版本號時間戳並發機制實現。
在Entity Framework的默認環境情況下,系統會使用合並方式處理並發
兩種鎖的使用場景
從上面對兩種鎖的介紹,我們知道兩種鎖各有優缺點,不可認為一種好於另一種,像樂觀鎖適用於寫比較少的情況下(多讀場景),即沖突真的很少發生的時候,這樣可以省去了鎖的開銷,加大了系統的整個吞吐量。但如果是多寫的情況,一般會經常產生沖突,這就會導致上層應用會不斷的進行return,這樣反倒是降低了性能,所以一般多寫的場景下用悲觀鎖就比較合適。
EF中的高並發
這里只介紹EF6中database-first開發方案的高並發解決方案,code-first開發方案中的高並發會在以后的EF CodeFirst系列中介紹。
EF默認支持樂觀並發:我們從數據庫加載了一條數據,這是有人修改了這條數據,而我們手中用的還是舊數據,這就出現了臟讀,這個時候我們修改了這條數據然后執行SaveChange()會發生什么呢?EF在保存數據時會首先查看數據庫中的數據有沒有改變過,數據沒有改變就執行保存;數據改變了會拋出異常,我們再次提交前必須解決沖突(提到解決沖突是不是想到了git提交中的沖突?EF中解決高並發的方法和git提交的方法采用的思想是一樣的,往下看就知道了)。
1.使用步驟
1.添加RowVersion列
在EF中database-first開發模式中,為了解決高並發問題我們可以為數據表填加一個timestamp類型的的列,列名為rowversion,rowversion是一個二進制的數據,在每次的添加/修改操作后rowversion的值都會變大
以Student實體為例,給Student表添加一個Rowversion列,類型為timestamp,如下所示:
2.生成/升級EMD
如果沒有EDM通過數據庫生成新的實體數據模型,如果有EDM則右擊設計器->Update Model From Database ->Refresh Student table,這時我們就可以在設計器中看到RowVersion屬性了,RowVersion屬性的Concurrency Mode設置為Fixed,如下圖
做完這兩步,EF API在執行Update時,會把RowVersion添加到where子句中(就像這樣:update tb set cloName=xxx where Id=@id and RowVersion=@rowversion),如果where子句中的RowVersion值和數據庫中的不一樣就拋出DbUpdateConcurrencyException。
2.一個栗子
Student student = null; using (var context = new SchoolDBEntities()) { student = context.Students.First(); } //修改學生名字 Console.Write("Enter New Student Name:"); student.StudentName = Console.ReadLine(); //Assigns student name from console using (var context = new SchoolDBEntities()) { try { context.Entry(student).State = EntityState.Modified; context.SaveChanges(); Console.WriteLine("修改成功!"); } catch (DbUpdateConcurrencyException ex) { Console.WriteLine("發生高並發異常!"); } }
假設有兩個用戶都在執行上邊的代碼,User1和User2拿到了同一個Student實例,User1打字快1秒就把這個Student的用戶修改了,並在數據庫保存成功(User1執行Update時RowVersion和數據庫一致,所以不報錯,保存完成后Student的RowVersion自動改變了),這時User2也完成了修改,在User2執行保存到數據庫時(生成的Update語句中的RowVersion和數據庫中不一致了,所以拋出異常)。
在並發拋出異常后可以根據業務需,向客戶端返回消息,
也可以直接處理沖突后的數據
a 保留最后一次對象修改的值 用 RefreshMode.ClientWins
b 保留最初的修改值 用 RefreshMode.StoreWins
c 合並修改值 針對同對象不同屬性一樣可以 用 RefreshMode.StoreWins
轉債:https://www.cnblogs.com/leslies2/archive/2012/07/30/2608784.html