redis分布式ロック原理解析の実現

21435 ワード

文書ディレクトリ
  • 1、分散ロックとは何ですか?
  • 2、redisによって実現する分散ロック
  • 3、内部実現解析
  • 3.1、redisにおけるデータ変化
  • 3.2、redissonの実現方式
  • 1、分散ロックとは何ですか.
    分散ロックは、分散システム間の共有リソースへの同期アクセスを制御する方法です.分散システムでは、各システム間の動作を調整する必要があることが多い.異なるシステムまたは同じシステムの異なるホスト間で1つまたは複数のリソースが共有されている場合、これらのリソースにアクセスする際には、互いに干渉しないように反発して一貫性を保証する必要があり、この場合、分散ロックを使用する必要があります.
    2、redis実現の分布式ロック
    redissonフレームワークを使用してredisコマンドを操作します.redissonはredlockアルゴリズムを実現し,非同期,公平性などの高度な機能を提供した.maven依存を追加します.
    <dependency>
        <groupId>org.redissongroupId>
        <artifactId>redissonartifactId>
        <version>3.11.1version>
    dependency>
    

    JAvaが実現したシングルマシンモードredisに基づく分散ロックは,クラスタモードがこのモードと類似しており,信頼性が高い
    public class RedisConfig {
        private static int count = 0; //               
        private String address = "127.0.0.1:6379";
        private String password = "961113";
    //    private String poolSize= "20";
    //    private String database= "10";
    
        public static void main(String[] args) throws InterruptedException {
            String address = "redis://127.0.0.1:6379";
    
            Config config = new Config();
            config.useSingleServer().setAddress(address).setPassword("961113");
            RedissonClient redisson = Redisson.create(config);
            System.out.println(redisson);
    
            int nThreads = 500; //      
            ExecutorService exec = Executors.newFixedThreadPool(nThreads);
    
            List tasks = new ArrayList(nThreads);
            for (int i = 0; i < nThreads; i++) {
                tasks.add((Callable) () -> {
                    Thread.sleep(10); //         
    
                    RLock lock = redisson.getLock("redis");
                    try {
                        boolean b = lock.tryLock(60, TimeUnit.SECONDS);
                        if (b) {
                            ++RedisConfig.count;
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    } finally {
                        //            
                        lock.unlock(); 
                    }
                    return RedisConfig.count;
                });
            }
            exec.invokeAll(tasks);
            exec.shutdown();
            while (true) {
                if (exec.isTerminated()) {
                    System.out.println("          !");
                    break;
                }
            }
            System.out.println("count = " + count);
            redisson.shutdown();
        }
    }
    

    output
    org.redisson.Redisson@1672fe87
              !
    count = 500
    

    3、内部実現解析
    3.1.redisにおけるデータ変化
    以org.redisson.RedissonLockのこの実装を解読します.上記のテストコード実行プロセスでは、redisにhashタイプの値が表示されます.ここで、「redis」はロック名であり、「redis」にはキー値のペアがあり、キーは「7 b 319 fef-0674-4 a 62-9 a 5 b-13 d 786 f 4 a 99 e:137」であり、値は「1」です.
    ここで、キーのフォーマットRedissonLockクラスは以下のようにUUID+threadId、valueは再入値となる.redissonは再入可能な分散ロックを実現し,この場合valueは実際の再入可能な値に対応する.
    protected String getLockName(long threadId) {
        return id + ":" + threadId;
    }
    

    redisのデータ
    127.0.0.1:6379> TYPE redis
    hash
    127.0.0.1:6379> HGETALL redis
    1) "7b319fef-0674-4a62-9a5b-13d786f4a99e:137"
    2) "1"
    127.0.0.1:6379> HGETALL redis
    1) "7b319fef-0674-4a62-9a5b-13d786f4a99e:95"
    2) "1"
    127.0.0.1:6379> HGETALL redis
    (empty list or set)
    127.0.0.1:6379> HGETALL redis
    1) "7b319fef-0674-4a62-9a5b-13d786f4a99e:93"
    2) "1"
    

    3.2.redissonの実現方式
    主な実装はluaスクリプトによって実装され,luaスクリプト自体は原子性をサポートする.解析org.redisson.RedissonLockでロックを試みたプロセス.
    ロックプロセス
    // KEYS[1]      key,    “redis”   
    // ARGV[1]       ,  30s。
    // ARGV[2]    set    (uuid + threadId),                  。
    //       key   ,    hset  (hset REDLOCK_KEY uuid+threadId 1),   pexpire      (        )
    //        KEY    ,  value   ,           ,       1,        
    //        KEY        
    <T> RFuture<T> tryLockInnerAsync(long leaseTime, TimeUnit unit, long threadId, RedisStrictCommand<T> command) {
        internalLockLeaseTime = unit.toMillis(leaseTime);
    
        return commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, command,
                  "if (redis.call('exists', KEYS[1]) == 0) then " +
                      "redis.call('hset', KEYS[1], ARGV[2], 1); " +
                      "redis.call('pexpire', KEYS[1], ARGV[1]); " +
                      "return nil; " +
                  "end; " +
                  "if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then " +
                      "redis.call('hincrby', KEYS[1], ARGV[2], 1); " +
                      "redis.call('pexpire', KEYS[1], ARGV[1]); " +
                      "return nil; " +
                  "end; " +
                  "return redis.call('pttl', KEYS[1]);",
                    Collections.<Object>singletonList(getName()), internalLockLeaseTime, getLockName(threadId));
    }
    

    ロック解除プロセス
    protected RFuture<Boolean> unlockInnerAsync(long threadId) {
        return commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
                //      ,  value   ,      ,    
                "if (redis.call('hexists', KEYS[1], ARGV[3]) == 0) then " +
                    "return nil;" +
                "end; " +
                //       ,      ,value   ,         ,     
                "local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1); " +
                //           0 ,        ,    
                "if (counter > 0) then " +
                    "redis.call('pexpire', KEYS[1], ARGV[2]); " +
                    "return 0; " +
                "else " +
                    //           0 ,   key,       。
                    "redis.call('del', KEYS[1]); " +
                    "redis.call('publish', KEYS[2], ARGV[1]); " +
                    "return 1; "+
                "end; " +
                "return nil;",
                //  5       KEYS[1],KEYS[2],ARGV[1],ARGV[2] ARGV[3]
                Arrays.<Object>asList(getName(), getChannelName()), LockPubSub.UNLOCK_MESSAGE, internalLockLeaseTime, getLockName(threadId));
    }
    
    /**
     *     、  、    。  !
     */