Session

net core 使用redis 分布式锁

电脑版发表于:2024/3/20 13:40

在电商项目中,必定会与订单打交道。订单中必定会涉及到扣库存,但是在高并发项目中,库存余量不能及时刷新,
导致库存扣除得不正确,1000个并发请求下来,扣除的库存只有几十个。如何解决此问题,那就得加锁了。
还有其他解决办法也能处理此问题,本文只分享一下redis 分布式锁解决此问题。


第一步添加一个redis的获取锁,与释放锁的方法。在获取锁时,如果未获取到锁,那么就会尝试重复获取,
直到获取倒锁后,才返回。当然在这步你可以加一个超时时间,例如最多尝试获取30秒,避免释放锁异常在此一直死循环。
释放锁时只有锁的拥有者才能释放锁,避免锁被其他线程释放了,这个分布式锁就没有意义了。


 public class RedisDistributedLock
 {
     private readonly ConnectionMultiplexer _redis;
     private readonly IDatabase _database;
 
     public RedisDistributedLock(ConnectionMultiplexer redis)
     {
         _redis = redis;
         _database = redis.GetDatabase();
     }
 
     public async Task<bool> AcquireLockAsync(string lockKey, string value, TimeSpan lockTimeout)
     {
         var acquired = await _database.StringSetAsync(lockKey, value, lockTimeout, When.NotExists);
         if (!acquired)
         {
             // 循环重试直到成功或超时
             var startTime = DateTime.UtcNow;
             while (true)
             {
                 Thread.Sleep(10); // 等待一段时间后再重试
 
                 if (await _database.StringSetAsync(lockKey, value, lockTimeout, When.NotExists))
                 {
                     acquired = true; // 成功获取到锁
                     break;
                 }
             }
         }
         return acquired;
     }
 
     public async Task ReleaseLockAsync(string lockKey, string value)
     {
         //验证锁的拥有者才能释放锁
         string currentValue = await _database.StringGetAsync(lockKey);
         if (currentValue == value)
         {
             await _database.KeyDeleteAsync(lockKey);
         }
     }
 }


订单扣库存场景


 private readonly ConnectionMultiplexer _connectionMultiplexer;
  //创建一个redis锁
  private readonly RedisDistributedLock _lockHelper;
  private readonly string _lockKey = "inventory_lock:OrderSubmit"; // 锁的唯一标识
  public OrdersService(IFreeSql freeSql, ICacheService cacheService, ConnectionMultiplexer connectionMultiplexer)
  {
      _freeSql = freeSql;
      _cacheService = cacheService;
      _connectionMultiplexer = connectionMultiplexer;
      _lockHelper = new RedisDistributedLock(_connectionMultiplexer);
  }
 
  public async Task<CreateOrdersResult> OrderSubmitAsync(CreateOrderModel arg)
  {
      var orderStatus = OrderStatus.Fail;
      var message = string.Empty;
      var lockValue = $"lock_{CodeHelper.CreateGuid()}";
      const int maxRetries = 5;
      TimeSpan retryDelay = TimeSpan.FromMilliseconds(100);
      if (await _lockHelper.AcquireLockAsync(_lockKey, lockValue, TimeSpan.FromSeconds(10)))
      {
          try
          {
              var oldInventory = await _freeSql.Select<Commodity>().Where(a => a.Code == arg.CommodityCode).FirstAsync();
              if (oldInventory.Inventory >= arg.OrderQuantity && oldInventory.Inventory > 0)
              {
                  var repo = _freeSql.GetRepository<Commodity>();
                  oldInventory.Inventory = oldInventory.Inventory - arg.OrderQuantity;
                  await repo.UpdateAsync(oldInventory);
                  orderStatus = OrderStatus.Success;
                  message = "下单成功";
              }
              else
              {
                  message = "下单失败,库存不足";
              }
          }
          catch (Exception e)
          {
              message = "下单失败," + e.Message;
          }
          finally
          {
              // 无论成功还是失败,最后都要释放锁
              await _lockHelper.ReleaseLockAsync(_lockKey, lockValue);
          }
      }
 
      var ordersRepo = _freeSql.GetRepository<Orders>();
      await ordersRepo.InsertAsync(new Orders()
      {
          Code = CodeHelper.CreateOrdersCode(),
          UserCode = arg.UserCode,
          CommodityCode = arg.CommodityCode,
          OrderTime = DateTime.Now,
          OrderAmount = arg.OrderAmount,
          OrderQuantity = arg.OrderQuantity,
          OrderStatus = orderStatus
      });
      return new CreateOrdersResult()
      {
          Status = true,
          Message = message
      };
 
  }


关于TNBLOG
TNBLOG,技术分享。技术交流:群号677373950
ICP备案 :渝ICP备18016597号-1
App store Android
精彩评论
{{item.replyName}}
{{item.content}}
{{item.time}}
{{subpj.replyName}}
@{{subpj.beReplyName}}{{subpj.content}}
{{subpj.time}}
猜你喜欢