net core 使用redis 分布式锁

在电商项目中,必定会与订单打交道。订单中必定会涉及到扣库存,但是在高并发项目中,库存余量不能及时刷新,导致库存扣除得不正确,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
      };

  }

  • 13
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring提供了对Redis分布式锁的支持,可以通过Spring的RedisTemplate或LettuceConnectionFactory来实现。下面是一个简单的实现示例: 1. 首先,在Spring配置文件中配置Redis连接池: ``` <bean id="redisConnectionFactory" class="org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory"> <constructor-arg name="hostName" value="${redis.host}"/> <constructor-arg name="port" value="${redis.port}"/> <constructor-arg name="password" value="${redis.password}"/> </bean> <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate"> <property name="connectionFactory" ref="redisConnectionFactory"/> <property name="keySerializer"> <bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/> </property> <property name="valueSerializer"> <bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/> </property> </bean> ``` 2. 然后,定义一个Redis分布式锁的工具类: ``` @Component public class RedisLockUtil { private static final long LOCK_EXPIRE_TIME = 30000; // 锁过期时间,单位毫秒 private static final String LOCK_PREFIX = "lock:"; // 锁前缀 @Autowired private RedisTemplate<String, String> redisTemplate; public boolean lock(String key) { String lockKey = LOCK_PREFIX + key; long now = System.currentTimeMillis(); long expireTime = now + LOCK_EXPIRE_TIME; Boolean result = redisTemplate.opsForValue().setIfAbsent(lockKey, String.valueOf(expireTime)); if (result != null && result) { redisTemplate.expire(lockKey, LOCK_EXPIRE_TIME, TimeUnit.MILLISECONDS); return true; } String oldExpireTime = redisTemplate.opsForValue().get(lockKey); if (oldExpireTime != null && Long.parseLong(oldExpireTime) < now) { String newExpireTime = redisTemplate.opsForValue().getAndSet(lockKey, String.valueOf(expireTime)); if (newExpireTime != null && newExpireTime.equals(oldExpireTime)) { redisTemplate.expire(lockKey, LOCK_EXPIRE_TIME, TimeUnit.MILLISECONDS); return true; } } return false; } public void unlock(String key) { String lockKey = LOCK_PREFIX + key; redisTemplate.delete(lockKey); } } ``` 该工具类定义了两个方法:lock和unlock。其中,lock方法实现了Redis分布式锁的获取,unlock方法实现了Redis分布式锁的释放。在lock方法中,首先将锁的过期时间设置为当前时间+30秒,并通过Redis的setIfAbsent方法尝试获取锁;如果获取成功,则返回true;否则,通过get方法获取锁的过期时间,如果锁已经过期,则通过getAndSet方法更新锁的过期时间并获取旧的过期时间,然后判断旧的过期时间是否等于获取到的过期时间,如果相等,则说明获取到了锁,返回true。在unlock方法中,直接通过delete方法删除锁。 3. 最后,在需要使用分布式锁的地方,注入RedisLockUtil即可使用分布式锁: ``` @Autowired private RedisLockUtil redisLockUtil; public void doSomethingWithLock(String key) { if (redisLockUtil.lock(key)) { try { // 获取锁成功后执行业务逻辑 // ... } finally { redisLockUtil.unlock(key); } } else { // 获取锁失败后的处理 // ... } } ``` 上面是一个简单的Spring实现Redis分布式锁的示例,如果您有更多的问题,请继续提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值