1. ホーム
  2. データベース
  3. レディス

redis の RedissonLock が待ちロックを実装する方法

2022-01-20 17:45:56

前置き

クエリー・インターフェースの初回問い合わせ時に、データを取得できなければ初期化メソッドを実行して初期化し、それ以降の問い合わせは直接ライブラリに問い合わせるようにします。この設計の目的は、初期化するデータが特に大きく、別のメソッドの呼び出しで処理できない場合、あるいは毎回初期化する必要がない場合、この場合は優先的にクエリのデータを最初に初期化することにあります。

問題点

この解答から、ある疑問が生まれます。クエリーインターフェースは自然なべき乗であることが知られており、私たちは追加のべき乗処理をする必要がないのです。しかし、この方式では、クエリは単なるクエリではない。初期化メソッドはクエリなしで実行しなければならないので、本質的には挿入ロジックである。そのため、独自にべき乗処理を行う必要がある。

スキーム

単一のサービスであれば、Javaのロックを使って、各データの主キーidをロックとして、idempotentを実装することができる。しかし、最近は基本的に分散サービスなので、前回の記事で述べたように、分散ロックRedissonLockを使って実装することができる。
最初のリクエストが同時に行われると、RedissonLockは競合し、ロックを取得した人が初期化メソッドを実行します。ロックを競合させないリクエストは、ロックが解放されるまでの待ち時間を設定することができます。ロック解放は、あなたが最初にデータがよく初期化されていない照会することができ、ライブラリに直接終了します。ここでは、どのようにRedissonLockを実装して待って言及することが重要ですか?

トライロック

RedissonLockはロックメソッドでwaitTimeというパラメータを提供するAPIを提供しており、このパラメータは待ち時間です。

public boolean tryLock(long waitTime, long leaseTime, TimeUnit unit)


メッセージはwaitTimeの間、redis自身のpublish-subscribe機能が使われ、購読されることになります。

RFuture<RedissonLockEntry> subscribeFuture = subscribe(threadId);
if (!subscribeFuture.await(time, TimeUnit.MILLISECONDS)) {
    if (!subscribeFuture.cancel(false)) {
        subscribeFuture.onComplete((res, e) -> {
            if (e == null) {
                unsubscribe(subscribeFuture, threadId);
            }
        });
    }
    acquireFailed(threadId);
    return false;
}


こうすることで、ロックが解除されると同時にメッセージが投稿されます。ロックを聞いているすべてのスレッドに通知され、これらのスレッドが再びロックを追加するために競争することで、必要なべき乗の機能が実現されるのです。ロックを解放するロジックを調べているのですが、メッセージを投稿しているのでしょうか?

unlockInnerAsync

protected RFuture<Boolean> unlockInnerAsync(long threadId) {
    return commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
            "if (redis.call('hexists', KEYS[1], ARGV[3]) == 0) then " +
                "return nil;" +
            "end; " +
            "local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1); " + "local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1); " +
            "if (counter > 0) then " +
                "redis.call('pexpire', KEYS[1], ARGV[2]); " +
                "return 0; " +
            "else " +
                "redis.call('del', KEYS[1]); " +
                "redis.call('publish', KEYS[2], ARGV[1]); " +
                "return 1; "+
            "end; " +
            "return nil;",
            Arrays.<Object>asList(getName(), getChannelName()), LockPubSub.UNLOCK_MESSAGE, internalLockLeaseTime, getLockName(threadId));

}

unlockInnerAsyncメソッドの中で、luaスクリプトが実行され、そのスクリプトの中で、publishコマンドが実行されたことが簡単にわかると思います。

について考える

Redissonはredisのパブリッシュ・サブスクライブ機能を巧みに利用して、分散ロック待ち機能を実装しています。では、実際のビジネス・アプリケーションでredisのパブリッシュ・サブスクライブ機能を使う場合、他にどのようなシナリオがあるでしょうか?コメント欄で自由に議論してください

RedisにRedissonLockを実装してロック待ちをする方法については、この記事が全てです。RedissonLockの詳細については、スクリプトハウスの過去記事を検索するか、以下の関連記事を引き続き閲覧してください。