Redis分散ロックの問題と解決策

3217 ワード

文書ディレクトリ

  • 実現構想:
  • 問題:
  • デッドロック
  • 転位解錠
  • 業務同時実行問題
  • 実現構想:

  • redis setIfAbsentロック
  • 論理実行が完了し、finallyはremoveを実行し、ロック
  • を解放する.

    質問:


    デッドロック


    ロックをかけるとダウンタイムになり、ロックを解除できません.ソリューション:ロックの有効期限を設定し、setNxと有効期限の設定操作の原子性を保証する必要があります.
  • Luaスクリプトファイルを実行して
  • を実装
  • RedisConnectionコマンド連用
  • (Boolean)redisTemplate.execute(new RedisCallback() {
             @Override
             public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
                 return connection.set(key.getBytes(), value.getBytes(), Expiration.seconds(timeout), RedisStringCommands.SetOption.ifAbsent());
             }
         });
    
  • 高バージョンRedisサポートメソッドを使用して
  • を連用

    位置ずれロック解除


    業務時間がロックタイムアウト時間より大きいスレッドaはロックを取得し、業務を実行し、過程でタイムアウトして失効する.スレッドbはこのとき実行を開始し、ロックを取得する.スレッドaの実行が完了し、ロックが解除される.このとき、スレッドbのロックがスレッドaによって解放されるという問題が発生する.
    ソリューション:ロックをかけると、現在のスレッドidが記録されます.ロック解除時には、現在のスレッドidとロック記録のスレッドidとが一致してこそ操作が許可される.ロック解除には、redisに保存されているロックレコードのスレッドidをクエリーする2つの操作が含まれます.redisのロックレコードを削除します.この2つの操作はluaスクリプトによって原子性を保証することができる.

    ビジネス同時実行の問題


    ビジネス時間はロックタイムアウト時間よりも大きく、ロックが失効した場合、複数のスレッドが同時にビジネスロジックを実行する可能性があり、このような状況を回避する必要がある場合は、分散ロック とすることができる.
    eg:
    // 
    luaResult = luaScript(lockName,currentValue,expire);
    
    if(luaResult){
       // , 
        System.out.println("Lock success,execute business,current time:" + System.currentTimeMillis());
        //     
        ExpandLockExpireTask expandLockExpireTask = new ExpandLockExpireTask(lockName,currentValue,expire,this);
        Thread thread = new Thread(expandLockExpireTask);
        thread.setDaemon(true);
        thread.start();
    
        Thread.sleep(600 * 1000);
    }
    
    /**
     *  
     * @author hzk
     * @date 2019/7/4
     */
    public class ExpandLockExpireTask implements Runnable {
    
        private String key;
        private String value;
        private long expire;
        private boolean isRunning;
        private LuaClusterLockJob2 luaClusterLockJob2;
    
        public ExpandLockExpireTask(String key, String value, long expire, LuaClusterLockJob2 luaClusterLockJob2) {
            this.key = key;
            this.value = value;
            this.expire = expire;
            this.luaClusterLockJob2 = luaClusterLockJob2;
            this.isRunning = true;
        }
    
        @Override
        public void run() {
            // 
            long waitTime = expire * 1000 * 2 / 3;
            while (isRunning){
                try {
                    Thread.sleep(waitTime);
                    if(luaClusterLockJob2.luaScriptExpandLockExpire(key,value,expire)){
                        System.out.println("Lock expand expire success! " + value);
                    }else{
                        stopTask();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    
        private void stopTask(){
            isRunning = false;
        }
    
    }
    

    ロックが成功した場合、デーモンスレッドが開きます.このスレッドは、ロックの有効期限が1/3残っている(時間は自分でビジネス定義に従うことができます)場合、ロックの継続を開始します.ビジネスロジックの実行が完了するまで、このプロセスを繰り返します.
    これにより、デッドロックを防止するとともに、業務時間が長すぎるロックの失効による同時問題を防止することができる.
    参考ブログ:https://blog.csdn.net/u013985664/article/details/94459529