C#:利用“事務+樂觀鎖+version”解決並發下的數據一致性問題


本文重點介紹通過事務控制,利用數據庫的樂觀鎖和時間戳,來解決並發(非高並發)環境下的臟讀、幻讀、不可重復讀等問題,同時也能解決超賣等現象,對開發企業管理系統的朋友提供一個思路,為更突出主題思路,文涉及到SqlSugar的一些代碼已隱去。

1. 數據庫建表

CREATE TABLE dbo.Test
(
      tId        INT IDENTITY NOT NULL
    , tName      NVARCHAR (20) NOT NULL
    , tSalary    DECIMAL (8, 2) NULL
    , tTimeStamp TIMESTAMP
    , PRIMARY KEY (tId)
)

2. 創建類

    public partial class Test
    {
        [SugarColumn(IsPrimaryKey =true,IsIdentity =true)]
        public int tId { get; set; }
        public string tName { get; set; }
        public decimal? tSalary { get; set; }
        [SugarColumn(IsOnlyIgnoreInsert = true)]
        public byte[] tTimeStamp { get; set; }
    }

3. 代碼示例

static async Task Main(string[] args)
{
	for (int i = 1; i <= 5; i++)
	{
		Task.Factory.StartNew(async (id) =>
		{
			await Test((int)id);
		}, i);
	}
}

static async Task Test(int threadID)
{
	var db = SqlSugar.DB;
	for (int k = 1; k <= 50; k++)
	{
		string log = string.Empty;
		log += $"第{threadID,2}線程,第{k}次";
		//客戶端從數據庫獲取數據
		var firstRead = await db.Queryable<Test>().SingleAsync(x => x.tId == 2);
		log += $"   name:{firstRead.tName,5} version:{BitConverter.ToString(firstRead.tTimeStamp).Replace(" - ", "")}";
		//客戶端修改數據需要時間
		Thread.Sleep(10);
		try
		{
			db.Ado.BeginTran();
			log += "    事務開始";
			//提交修改前數據進行驗證
			var secondRead = await db.Queryable<Test>().SingleAsync(x => x.tId == 2);
			if (BitConverter.ToString(secondRead.tTimeStamp) != BitConverter.ToString(firstRead.tTimeStamp))
			{
				log += $"    不可重復讀,version:{BitConverter.ToString(secondRead.tTimeStamp).Replace(" - ", "")}";
				throw new Exception();
			}
			var data = new Test { tId = 2, tName = $"{threadID}-{k}" };
			var result = await db.Updateable(data).Where(c => c.tTimeStamp == firstRead.tTimeStamp).IgnoreColumns(ignoreAllNullColumns: true).ExecuteCommandAsync();
			db.Ado.CommitTran();
			log += result > 0 ? $"    修改成功,當前name:{data.tName}" : "    修改失敗,數據被其它線程修改";
		}
		catch (Exception)
		{
			db.Ado.RollbackTran();
			log += "    事務回滾";
		}
		finally
		{
			Console.WriteLine(log);
		};
		Thread.Sleep(10);
	}
}

運行結果


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM