首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> ASP.NET 基礎 >> ASP.NET 基礎

.NET 事務併發異常深度解析:髒讀、不可重複讀、幻讀與隔離級別對照表

     編輯:ASP.NET 基礎

三大併發異常

異常定義生活比喻
髒讀 事務 A 修改未提交時,事務 B 讀取了該修改。A 後來可能回滾。 A 未提交代碼發給 B,A 後來撤銷修改,B 看到的代碼不再存在。
不可重複讀 事務 A 兩次讀取同一數據,期間事務 B 修改並提交,導致兩次結果不一致。 A 審查代碼時獲取兩次,中間 B 修改了已審查部分,A 發現不一致。
幻讀 事務 A 兩次查詢同一範圍,期間事務 B 新增/刪除滿足條件的行,導致結果集行數變化。 A 統計文件數兩次,中間 B 新增文件,兩次統計數不一致。

代碼驗證

使用 TransactionScope 指定隔離級別,多線程並發測試:

髒讀測試

[TestMethod] public void DirtyRead_Test()
{
    var opt1 = new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted }; // 寫事務
    var opt2 = new TransactionOptions { IsolationLevel = IsolationLevel.ReadUncommitted }; // 讀事務
    using (var ts1 = new TransactionScope(TransactionScopeOption.Required, opt1))
    {
        using (var ctx = new TestEntities()) { ctx.Tables.Add(new Table{Name="段光偉"}); ctx.SaveChanges(); }
        var evt = new AutoResetEvent(false);
        ThreadPool.QueueUserWorkItem(_ => {
            using (var ts2 = new TransactionScope(TransactionScopeOption.Required, opt2))
            using (var ctx = new TestEntities()) { Assert.AreEqual(2, ctx.Tables.Count()); } // 髒讀發生
            evt.Set();
        });
        evt.WaitOne(); // 不 Complete,事務回滾
    }
    // 回滾後驗證數據未持久化
    using (var ctx = new TestEntities()) Assert.AreEqual(1, ctx.Tables.Count());
}

不可重複讀測試

[TestMethod] public void NonRepeatableRead_Test()
{
    var opt = new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted };
    using (var ts1 = new TransactionScope(TransactionScopeOption.Required, opt))
    {
        using (var ctx = new TestEntities()) Assert.AreEqual("李妞妞", ctx.Tables.First().Name);
        var evt = new AutoResetEvent(false);
        ThreadPool.QueueUserWorkItem(_ => {
            using (var ts2 = new TransactionScope(TransactionScopeOption.Required, opt))
            using (var ctx = new TestEntities()) { ctx.Tables.First().Name = "段光偉"; ctx.SaveChanges(); ts2.Complete(); }
            evt.Set();
        });
        evt.WaitOne();
        using (var ctx = new TestEntities()) Assert.AreEqual("段光偉", ctx.Tables.First().Name); // 不可重複讀
    }
}

幻讀測試

[TestMethod] public void PhantomRead_Test()
{
    var opt1 = new TransactionOptions { IsolationLevel = IsolationLevel.RepeatableRead };
    var opt2 = new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted };
    using (var ts1 = new TransactionScope(TransactionScopeOption.Required, opt1))
    {
        using (var ctx = new TestEntities()) Assert.AreEqual(1, ctx.Tables.Count());
        var evt = new AutoResetEvent(false);
        ThreadPool.QueueUserWorkItem(_ => {
            using (var ts2 = new TransactionScope(TransactionScopeOption.Required, opt2))
            using (var ctx = new TestEntities()) { ctx.Tables.Add(new Table{Name="段光偉"}); ctx.SaveChanges(); ts2.Complete(); }
            evt.Set();
        });
        evt.WaitOne();
        using (var ctx = new TestEntities()) Assert.AreEqual(2, ctx.Tables.Count()); // 幻讀
    }
}

四大隔離級別對併發異常的控制能力

隔離級別髒讀不可重複讀幻讀
Read Uncommitted (讀未提交)允許允許允許
Read Committed (讀已提交)禁止允許允許
Repeatable Read (可重複讀)禁止禁止允許
Serializable (串行化)禁止禁止禁止

選型建議

  • 默認 Read Committed 平衡性能與一致性(SQL Server/Oracle/PostgreSQL 默認)。
  • 需防止不可重複讀用 Repeatable Read(MySQL InnoDB 默認,通過 Next-Key Lock 部分防幻讀)。
  • 極高一致性要求(如財務對賬)用 Serializable,但並發性能顯著下降。
  • 避免使用 Read Uncommitted,除非極度容忍髒數據的分析類查詢。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved