减库存问题 实践

我用sql server 做了 一个模拟 

一张订单表 

一张产品表

逻辑是准备一个存储过程   产品数量>下单数   就可以 往 订单表insert 一条记录   产品数量 -=下单数  ;

这个 存储过程 没添加 事务 没指定 会话隔离级别 也没有 加锁

ALTER  PROCEDURE [dbo].[pro_createorder_auto]
@num int,
@errormsg nvarchar(1000) output
AS
BEGIN
DECLARE @res int;
 
	  BEGIN TRY
	   --         set transaction  isolation level  repeatable read ;
				--begin tran
					if   exists(select 1 from T_goods  where id = 1 and [goods_num] >=@num)
					begin
						update T_goods   set goods_num -=@num    where id  =1;
						insert into t_orders (goods_id,goods_num)values( 1,@num );
						select @res = 1;
					end
					    select @res = 0;	
				--commit tran;
				return @res; 	

		END TRY-----------结束捕捉异常
		BEGIN CATCH------------有异常被捕获
		 select  @errormsg = error_message() -- 将异常信息付给 output 参数
			IF @@TRANCOUNT > 0---------------判断有没有事务
			BEGIN
				ROLLBACK TRAN----------回滚事务
			END 
			return 0; 	
		END CATCH--------结束异常处理
	
END

然后用C# 模拟并发 :用 1-到4的随机数下单

代码:

 class Program
    {
        static void Main(string[] args)
        {

            // 只有10万个商品  多线程模拟并发下单 100 万次
            var radom = new Random();
            var list = new List<IAsyncResult>();
            for (int i = 0; i < 10 * 10000; i++)
            {
                var doSomething = new Action(() => { CreateOrder1(radom.Next(1,5)); });
                var res = doSomething.BeginInvoke(null,null);
                list.Add(res);
            }

            while (list.Exists(x=>x.IsCompleted== false))
            {
                Console.WriteLine("还没执行完 再等等!");
                Thread.Sleep(10000);
            }
          
            Console.WriteLine("执行完毕!!!");
            Console.WriteLine("库存数量" + GoodsNum());
            Console.WriteLine("订单数量" + OrderNum());
            Console.ReadLine();
        }

        static int SccceedNum { get; set; }
        static int ErrorNum { get; set; }
        static int OtherNum { get; set; }
        static private readonly object lockobj = new object();

        /// <summary>
        /// 不加控制的生成订单,只要库存>0
        /// </summary>
        static void CreateOrder1(int num)
        {
                var pram = new List<SqlParameter>() { new SqlParameter("@num",num )};
                var temp = new SqlParameter("@errormsg", SqlDbType.NVarChar, 1000 );
                temp.Direction = ParameterDirection.Output;
                pram.Add(temp);
                var returnInt = new SqlParameter("res", SqlDbType.Int);
                returnInt.Direction = ParameterDirection.ReturnValue;
                pram.Add(returnInt);
                int res = 0;
                res = sqlHelper.ExecuteNonQuery(CommandType.StoredProcedure, "pro_createorder_auto", pram.ToArray());
                if (!string.IsNullOrWhiteSpace(  temp.Value.ToString()))
                {
                    Console.WriteLine("结果:{0},错误消息:{1}", res.ToString(), temp.Value.ToString());
                }
        }

        /// <summary>
        /// 查询当前库存数
        /// </summary>
        /// <returns></returns>
        static string GoodsNum()
        {
            return  sqlHelper.ExecuteScalar("select goods_num   from T_goods where id = 1 ").ToString();
        }

        /// <summary>
        /// 查询当前订单数
        /// </summary>
        /// <returns></returns>
        static string OrderNum()
        {
            return sqlHelper.ExecuteScalar("select  count(1) num   from T_orders where  goods_id = 1 ").ToString();
        }
    }

最后 肾宝 溢出了 12单 

后面打算在 存储过程中 加上  隔离界别  和 事务  

ALTER  PROCEDURE [dbo].[pro_createorder_auto]
@num int,
@errormsg nvarchar(1000) output
AS
BEGIN
DECLARE @res int;
 
	  BEGIN TRY
	            set transaction  isolation level  repeatable read ;
				begin tran
					if   exists(select 1 from T_goods  where id = 1 and [goods_num] >=@num)
					begin
						update T_goods   set goods_num -=@num    where id  =1;
						insert into t_orders (goods_id,goods_num)values( 1,@num );
						select @res = 1;
					end
					    select @res = 0;	
				commit tran;
				return @res; 	

		END TRY-----------结束捕捉异常
		BEGIN CATCH------------有异常被捕获
		 select  @errormsg = error_message() -- 将异常信息付给 output 参数
			IF @@TRANCOUNT > 0---------------判断有没有事务
			BEGIN
				ROLLBACK TRAN----------回滚事务
			END 
			return 0; 	
		END CATCH--------结束异常处理
	
END

现在发现 死锁太频繁了可怕  虽然订单数一直在增长 ,但是死锁的几率太大了

这要是做成 产品 肯定会被骂死吧  这代表能否 下单成功是靠运气的 ,一旦别人在下单 我就会失败,严重影响购买者的心情

我再 改成 事务 加 with (holdlock) 的方式 结果是一样的 大量死锁 ,看来在高并发的时候给数据库事务加锁也不是一个好办法

那我想用 程序的方式来实现一下锁 这边的存储过程暂时不改

程序中以后  就不会在数据库中争夺资源了,但是有大量并发时 我就是那第10万个 我的 页面得 loading 多久呀

有时候想想还不如直接有个结果 ,行就行不行就不行 ,老是 loading 耐心有限呀

到这 以什么姿势来处理并发 成了我的疑问

等我后续学一下 mq 再回来 看看这个问题

猜你喜欢

转载自blog.csdn.net/qq_36445227/article/details/108847376