ホームページ  >  記事  >  データベース  >  Redis分散ロックの実装方法

Redis分散ロックの実装方法

WBOY
WBOYオリジナル
2023-05-10 21:01:345049ブラウズ

Redis は、データの保存と読み取りができるオープンソースのメモリ内データ キャッシュ システムです。分散環境では、複数のアプリケーションが同じリソース上で同時に動作すると、ダーティ データやデータの不整合に関する問題が発生する可能性があります。この問題を解決するには、分散ロックを導入してデータの一貫性を確保します。

この記事は、Redis 分散ロックのアプリケーション シナリオ、原則、​​実装方法を紹介することで、読者が Redis を使用して分散ロックを実装する方法を理解するのに役立ちます。

1. アプリケーション シナリオ

分散システムでは、アプリケーションは複数のリソースを同時に操作する必要がある場合があります。では、このアプリケーションのリソース操作がスレッドセーフであることを確認するにはどうすればよいでしょうか?現時点では、分散ロックを導入する必要があります。

分散ロックを使用すると、次の問題を解決できます。

(1) 複数のクライアントが同じリソースを同時に変更して、データの不整合が生じることを回避します。

(2) ネットワーク遅延やその他の問題により、クライアントが同じリソースに複数の変更を加えることを防ぎます。

(3) クライアントがリソースを長時間占有して、他のクライアントがリソースに正常にアクセスできなくなることを防止します。

2. 原則

Redis 分散ロックは主に setnx コマンドを通じて実装されます。 setnx コマンドは Redis のアトミック操作であり、複数のクライアントの同時操作において、1 つのクライアントだけがキーと値のペアを Redis に正常に設定できるようにします。

次に、Redis 分散ロックの具体的な実装を見てみましょう。

3. 実装方法

(1) ロックの取得

ロックの取得処理では、setnx コマンドを使用してキーと値のペアを設定する必要があります。 。設定が成功すればロックを取得できたことになりますが、設定に失敗した場合は一定時間待ってから再度ロックの取得を試みる必要があります。

まず、次のコード ブロックを通じてロックを取得します。

boolean lock = jedis.setnx(key, value) == 1;

このうち、key と value はそれぞれロックの名前と値を表し、jedis は Redis クライアントを表します。

ロックの名前が Redis に存在しない場合、上記のコードの戻り値は 1 で、設定が成功し、ロックが取得されたことを示します。ロック名がすでに Redis に存在する場合、上記のコードの戻り値は 0 となり、設定が失敗し、ロックの取得が失敗したことを示します。

(2) ロックを解放する

ロックを解放するプロセスでは、del コマンドを使用して Redis のキーと値のペアを削除する必要があります。

まず、次のコード ブロックを通じてロックを解放します。

long result = jedis.del(key);

このうち、key はロックの名前を表し、jedis は Redis クライアントを表します。

Redis のキーと値のペアが正常に削除された場合、上記のコードの戻り値は 1 で、ロックが正常に解放されたことを示します。キーと値のペアが Redis に存在しない場合、上記のコードの戻り値は 0 で、ロックの解放が失敗したことを示します。

(3) ロックの有効期限を設定する

ロックが常に占有されることを防ぐために、ロックの有効期限を設定する必要があります。ロック所有者が一定期間内にロックを解放しない場合、Redis はロックが永久に占有されるのを防ぐために自動的にロックを削除します。

まず、次のコード ブロックを通じてロックの有効期限を設定する必要があります:

jedis.expire(key, timeout);

このうち、key はロックの名前を表し、timeout はロックの有効期限を表します。数秒でロックします。

他のクライアントのロックを誤って削除しないようにするには、ロックの値が、ロックを取得したときに設定した値と一致しているかどうかを確認する必要があります。

String value = jedis.get(key);
if (StringUtils.isNotBlank(value) && value.equals(uuid)) {
  jedis.del(key);
}

このうち、uuid はロックを取得するクライアントの一意の ID を表します。

(4) 他のクライアントのロックを誤って削除しないようにする

ロックを使用した後は、正しくロックを解除する必要があります。そうしないと、他のクライアントのロックが誤って削除されてしまいます。

したがって、他のクライアントのロックが誤って削除されるのを防ぐために、コードに一意の識別子を追加する必要があります。

まず、ロックを取得するプロセスで、以下に示すように、クライアントの一意の識別子を生成する必要があります。

String uuid = UUID.randomUUID().toString();

次に、ロックを取得して解放するプロセスで、ロックの場合、現在のクライアントがロックを取得したかどうかを判断するには、キーに対応する値が uuid と等しいかどうかを判断する必要があります。ロックの取得とロックの解放のプロセスでは、uuid を次のように設定する必要があります。 value をキーに対応する値に置き換えます。

具体的なコードは次のとおりです。

boolean lock = jedis.setnx(key, uuid) == 1;
if (lock) {
  jedis.expire(key, timeout);
}

// 释放锁
String value = jedis.get(key);
if (StringUtils.isNotBlank(value) && value.equals(uuid)) {
  jedis.del(key);
}

(5) 間違った使用例

分散ロックを使用する過程で、次のような状況が発生した場合、 Deadlock:

// 获取锁
jedis.setnx(key, value);

// 不释放锁

したがって、ロックを使用するときは、ロックを正しく解放することに注意する必要があります。そうしないと、システムに予期しない結果が生じます。

(6) 実装クラス

最後に、上記のコードを Redis 分散ロック クラスにカプセル化する方法を見てみましょう。

import redis.clients.jedis.Jedis;

import java.util.UUID;

public class RedisLock {

    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";

    private Jedis jedis;

    public RedisLock(Jedis jedis) {
        this.jedis = jedis;
    }

    /**
     * 尝试获取分布式锁
     * @param key 锁
     * @param requestId 请求标识
     * @param expireTime 超期时间(秒)
     * @return 是否获取成功
     */
    public boolean tryGetDistributedLock(String key, String requestId, int expireTime) {
        String result = jedis.set(key, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
        return LOCK_SUCCESS.equals(result);
    }

    /**
     * 释放分布式锁
     * @param key 锁
     * @param requestId 请求标识
     * @return 是否释放成功
     */
    public boolean releaseDistributedLock(String key, String requestId) {
        String value = jedis.get(key);
        if (value != null && value.equals(requestId)) {
            jedis.del(key);
            return true;
        }
        return false;
    }

    /**
     * 获取请求标识
     * @return 请求标识
     */
    public static String getRequestId() {
        return UUID.randomUUID().toString();
    }

}

この時点で、Redis 分散ロックの実装が完了しました。

4. 概要

この記事は、Redis 分散ロックのアプリケーション シナリオ、原則、​​実装方法を紹介することで、読者が Redis を使用して分散ロックを実装する方法を理解するのに役立ちます。分散ロックの実装は比較的複雑であるため、ロックの値が取得時に設定された値と一致するかどうかを判断したり、取得のプロセス中にキーの値として uuid を設定したりするなど、いくつかの詳細に注意を払う必要があります。対応する値は中です。分散ロックを正しく使用することによってのみ、分散システム内のデータの一貫性と信頼性を確保できます。

以上がRedis分散ロックの実装方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。