分布式redislock使用注意事項


采用技術框架:csredis 

業務邏輯:單個數據做判重,不重復增加,后續update

實現:使用redislock +分布式redis key的方式雙重機制

問題:一個過程耗時72s

代碼:

public async Task<long> AddBasicCustomerLog(BaseEmpInfo empInfo, long buid, DateTime dataTime, IDbConnection connection, IDbTransaction transaction = null)
        {
             
            var stopWatch = new Stopwatch();
            stopWatch.Start();
            StringBuilder stringBuilder = new StringBuilder("【AddBasicCustomerLog】buid=" + buid);
            long res = 0;
            string token = DateTime.Now.ToLongTimeString() + Guid.NewGuid().ToString();
            string lockKey = LockKeyCustomerLog + buid ;
            CSRedisClientLock lockRedis = null;
            //這塊還是要釋放,不然還是有問題,會導致程序占用的太多了 todo
            int i = 0;
            while (lockRedis == null)
            {
                lockRedis = RedisHelper.Lock(lockKey, 15);

                //lockRedis = await _database.LockTakeAsync(lockKey, token, TimeSpan.FromSeconds(15));
                if (lockRedis == null)
                {
                    //if (connection.State != ConnectionState.Closed)
                    //{
                    //    connection.Close();
                    //}
                    Thread.Sleep(3000);//等待3s
                }
                i++;
                if (i > 3)
                {
                    _logger.LogCritical(i + "次獲取鎖,依然失敗,本次放棄對buid:" + buid + "的新增CustomerLog事件");
                    return res;
                }
            }
            if (connection.State != ConnectionState.Open)
            {
                connection.Open();
            }
            stringBuilder.AppendLine("2拿鎖" + stopWatch.Elapsed.TotalSeconds);
            //獲取鎖后再次查看是否已有  如果沒有就新增
            var statisDefeat = await _statisCustomerLogRepository.GetStatisCustomerIdByEmpId(buid, dataTime, connection, transaction);
            stringBuilder.AppendLine("3GetStatisCustomerIdByEmpId-" + stopWatch.Elapsed.TotalSeconds);
            if (statisDefeat <= 0)
            {
                res = await _statisCustomerLogRepository.SavBasicRecord(empInfo, buid, dataTime, connection, transaction);
                stringBuilder.AppendLine("4SavBasicRecord-" + stopWatch.Elapsed.TotalSeconds);
            }
            else
            {
                return statisDefeat;
            }

            //await _database.LockReleaseAsync(lockKey, token);
            lockRedis?.Unlock();

            stopWatch.Stop();
            stringBuilder.AppendLine("3結束" + stopWatch.Elapsed.TotalSeconds);
            if (stopWatch.Elapsed.TotalSeconds > 2)
            {
                _logger.LogInformation(stringBuilder.ToString());
            }
            return res;
        }

  問題1:如果是一個已存在的數據 那么 可能存在沒有釋放lock , return statisDefeat;這一步

       問題2:lock 沒有釋放,自動延期

       問題3:lockrediskey 不唯一

 /// <summary>開啟分布式鎖,若超時返回null</summary>
  /// <param name="name">鎖名稱</param>
  /// <param name="timeoutSeconds">超時(秒)</param>
  /// <param name="autoDelay">自動延長鎖超時時間,看門狗線程的超時時間為timeoutSeconds/2 , 在看門狗線程超時時間時自動延長鎖的時間為timeoutSeconds。除非程序意外退出,否則永不超時。</param>
  /// <returns></returns>
  public static CSRedisClientLock Lock(
    string name,
    int timeoutSeconds,
    bool autoDelay = true)
  {
    return RedisHelper<TMark>.Instance.Lock(name, timeoutSeconds, true);
  }
/// <summary>開啟分布式鎖,若超時返回null</summary>
    /// <param name="name">鎖名稱</param>
    /// <param name="timeoutSeconds">超時(秒)</param>
    /// <param name="autoDelay">自動延長鎖超時時間,看門狗線程的超時時間為timeoutSeconds/2 , 在看門狗線程超時時間時自動延長鎖的時間為timeoutSeconds。除非程序意外退出,否則永不超時。</param>
    /// <returns></returns>
    public CSRedisClientLock Lock(string name, int timeoutSeconds, bool autoDelay = true)
    {
      name = "CSRedisClientLock:" + name;
      DateTime now = DateTime.Now;
      while (DateTime.Now.Subtract(now).TotalSeconds < (double) timeoutSeconds)
      {
        string str = Guid.NewGuid().ToString();
        if (this.Set(name, (object) str, timeoutSeconds, new RedisExistence?(RedisExistence.Nx)))
          return new CSRedisClientLock(this, name, str, timeoutSeconds, autoDelay);
        Thread.CurrentThread.Join(3);
      }
      return (CSRedisClientLock) null;
    }

  解決方案:1:return 前一定釋放lock 2:redislock設置為可過期的 3:設置rediskey的時候設置成業務唯一的

新代碼

public async Task<long> AddBasicCustomerLog(BaseEmpInfo empInfo, long buid, long compId, DateTime dataTime, IDbConnection connection, IDbTransaction transaction = null)
        {
             
            var stopWatch = new Stopwatch();
            stopWatch.Start();
            StringBuilder stringBuilder = new StringBuilder("【AddBasicCustomerLog】buid=" + buid);
            long res = 0;
            string token = DateTime.Now.ToLongTimeString() + Guid.NewGuid().ToString();
            string lockKey = LockKeyCustomerLog + buid + "_" + compId;
            CSRedisClientLock lockRedis = null;
            //這塊還是要釋放,不然還是有問題,會導致程序占用的太多了 todo
            int i = 0;
            while (lockRedis == null)
            {
                lockRedis = RedisHelper.Lock(lockKey, 15, false);

                //lockRedis = await _database.LockTakeAsync(lockKey, token, TimeSpan.FromSeconds(15));
                if (lockRedis == null)
                {
                    //if (connection.State != ConnectionState.Closed)
                    //{
                    //    connection.Close();
                    //}
                    Thread.Sleep(3000);//等待3s
                }
                i++;
                if (i > 3)
                {
                    _logger.LogCritical(i + "次獲取鎖,依然失敗,本次放棄對buid:" + buid + "的新增CustomerLog事件");
                    return res;
                }
            }
            if (connection.State != ConnectionState.Open)
            {
                connection.Open();
            }
            stringBuilder.AppendLine("2拿鎖" + stopWatch.Elapsed.TotalSeconds);
            //獲取鎖后再次查看是否已有  如果沒有就新增
            var statisDefeat = await _statisCustomerLogRepository.GetStatisCustomerIdByEmpId(buid, dataTime, connection, transaction);
            stringBuilder.AppendLine("3GetStatisCustomerIdByEmpId-" + stopWatch.Elapsed.TotalSeconds);
            if (statisDefeat <= 0)
            {
                res = await _statisCustomerLogRepository.SavBasicRecord(empInfo, buid, dataTime, connection, transaction);
                stringBuilder.AppendLine("4SavBasicRecord-" + stopWatch.Elapsed.TotalSeconds);
            }
            else
            {
                res = statisDefeat;
            }

            //await _database.LockReleaseAsync(lockKey, token);
            lockRedis?.Unlock();

            stopWatch.Stop();
            stringBuilder.AppendLine("3結束" + stopWatch.Elapsed.TotalSeconds);
            if (stopWatch.Elapsed.TotalSeconds > 2)
            {
                _logger.LogInformation(stringBuilder.ToString());
            }
            return res;
        }

  


免責聲明!

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



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