zoukankan      html  css  js  c++  java
  • 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);
    	}
    }
    

    运行结果

  • 相关阅读:
    杂记5
    杂记4
    杂记3
    杂记2
    杂记1
    也来个网页版本的五子棋
    验证码识别
    npm publish命令
    window nginx php ci框架环境搭建
    也来个网页版本的五子棋
  • 原文地址:https://www.cnblogs.com/zhaoshujie/p/12294718.html
Copyright © 2011-2022 走看看