Redis分散ロック

2415 ワード

前言
分散ロックには一般的に3つの実現方式がある:1.データベース楽観ロック;2.Redisベースの分散ロック;3.ZooKeeperベースの分散ロック.このブログでは、Redisに基づいて分散ロックを実現する第2の方法について説明します.このブログでは、Redis分散ロックの実装について様々なブログが紹介されていますが、彼らの実装には様々な問題があり、子弟の誤解を避けるために、Redis分散ロックを正しく実装する方法について詳しく説明します.
しんらいせい
まず、分散ロックが使用可能であることを確認するために、少なくともロックの実装が以下の4つの条件を満たしていることを確認します.
  • 相互反発性.任意の時点で、ロックを保持できるクライアントは1つしかありません.
  • デッドロックは発生しません.ロックを保持している間にクラッシュし、アクティブにロックを解除しなくても、後続の他のクライアントがロックを追加できることを保証します.
  • はフォールトトレランスを有する.ほとんどのRedisノードが正常に動作している限り、クライアントはロックとロック解除を行うことができます.
  • ベルを解くにはベルを結ぶ人が必要です.ロックとロック解除は同じクライアントでなければなりません.クライアントは自分で他の人のロックを解除することはできません.

  • コード実装
    コンポーネント依存
    まず、JedisオープンソースコンポーネントをMavenで導入し、pom.xmlファイルに次のコードを追加します.
    
        redis.clients
        jedis
        2.9.0
    

    ここでrequestId==は一意性を有し、重複しない.例えば、UUID
    鍵を掛ける.
    public class RedisTool {
    
        private static final String LOCK_SUCCESS = "OK";
        private static final String SET_IF_NOT_EXIST = "NX";
        private static final String SET_WITH_EXPIRE_TIME = "PX";
    
        /**
         *         
         * @param jedis Redis   
         * @param lockKey  
         * @param requestId     
         * @param expireTime     
         * @return       
         */
        public static boolean tryGetDistributedLock(Jedis jedis, String lockKey, String requestId, int expireTime) {
    
            String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
    
            if (LOCK_SUCCESS.equals(result)) {
                return true;
            }
            return false;
    
        }
    
    }

    ロック解除:
    public class RedisTool {
    
        private static final Long RELEASE_SUCCESS = 1L;
    
        /**
         *       
         * @param jedis Redis   
         * @param lockKey  
         * @param requestId     
         * @return       
         */
        public static boolean releaseDistributedLock(Jedis jedis, String lockKey, String requestId) {
    
            String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
            Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));
    
            if (RELEASE_SUCCESS.equals(result)) {
                return true;
            }
            return false;
    
        }
    
    }