ホームページ  >  記事  >  バックエンド開発  >  PHP で Redis を使用して分散ロックのインテリジェントなスイッチングを実装する

PHP で Redis を使用して分散ロックのインテリジェントなスイッチングを実装する

王林
王林オリジナル
2023-05-22 08:36:05838ブラウズ

分散システムでは、複数のノードが同じリソース上で同時に動作するため、同時実行性の競合が発生しやすくなります。この問題を解決するには、通常、分散ロックを使用して共有リソースへのアクセスを制御します。 Redis は、分散ロックの実装に使用できる効率的な分散キャッシュです。この記事では、PHP で Redis を使用して分散ロックを実装し、インテリジェントなスイッチングを実現する方法を紹介します。

1. 分散ロックとは何ですか?

分散ロックとは、複数のノードが共有リソースにアクセスしているときに、1 つのノードだけがリソースを操作でき、他のノードは待機する必要があることを意味します。分散ロックには、ロックの取得とロックの解放という 2 つの操作が含まれます。ロックを取得する操作では、スレッドの安全性を確保し、ロックの繰り返し取得を回避する必要があり、ロックの解放操作では、デッドロックを防ぐためにロックが正しく解放されることを保証する必要があります。

2. Redis を使用して分散ロックを実装する

Redis は、データに迅速にアクセスでき、分散をサポートするインメモリ データベースです。 Redisではsetコマンドを使用してキーと値を設定できますが、キーが存在しない場合は設定に成功しますが、キーがすでに存在する場合は設定に失敗します。この機能を使用して、分散ロックを実装できます。

具体的な実装は 2 つのステップに分けることができます。最初のステップはロックの取得です。set コマンドを使用してキーを設定します。設定が成功した場合は、ロックが正常に取得されたことを意味します。そうでない場合は、ロックが他のノードによって取得されたことを意味します。 2 番目のステップでは、ロックを解除し、del コマンドを使用してキーを削除します。

次は、Redis を使用して分散ロックを実装する PHP サンプル コードです:

class RedisLock{
    protected $redis;
    protected $lockKey = 'distributed_lock';
    protected $timeout = 30; // 默认锁超时时间30s

    function __construct(){
        $this->redis = new Redis();
        $this->redis->connect('127.0.0.1', 6379);
    }

    function getRedisClient(){
        return $this->redis;
    }

    function setLock($key = ''){
        if(empty($key)){
            $key = $this->lockKey;
        }else{
            $key = $this->lockKey . '_' . $key; // 如果有传入锁键名,则拼接上前缀方便管理
        }

        $timeout = $this->timeout;

        while ($timeout > 0) { // 如果锁未超时,则等待获取锁
            $result = $this->redis->set($key, time() + $timeout, ['nx', 'ex' => $timeout]);

            if ($result){
                return true;
            }

            sleep(1); // 每次等待1秒钟
            $timeout = $timeout -1;
        }

        return false;
    }

    function unlock($key = ''){
        if(empty($key)){
            $key = $this->lockKey;
        }else{
            $key = $this->lockKey . '_' . $key; // 如果有传入锁键名,则拼接上前缀方便管理
        }

        return $this->redis->del($key);
    }
}

このクラスを使用してロックを簡単に取得します:

$lock = new RedisLock();
if($lock->setLock()){
    // 执行需要获得锁的操作
    $lock->unlock();
}

3. インテリジェントなロック切り替えを実装します。

上記の分散ロックの実装は比較的単純ですが、ノード障害が発生すると問題が発生します。たとえば、タイミング: ノード A がロックを取得するが、運用中に障害が発生し、ロックが正常に解放できなくなり、このときノード B がロックを取得しようとしますが、ノード A がロックを保持したままなので失敗します。 。この問題を解決するには、インテリジェントなロック切り替えを実装できます。

まず、ロック タイムアウトのリセット メカニズムを実装する必要があります。つまり、ロックがタイムアウトになった後、ロックは使用可能な状態にリセットされます。これは、スケジュールされたタスクを通じて実現できます。次に、ロック保持者を記録し、異常事態が発生した場合に手動でロック保持者を解除できるようにする必要があります。最後に、ロック切り替えメカニズムを実装する必要があります。つまり、ロックを保持しているノードに障害が発生した場合、他のノードが自動的にロック保持者に切り替えることができます。

具体的な実装については、次のサンプル コードを参照してください。

class RedisLock {
    protected $redis;
    protected $lockKey = 'distributed_lock';
    protected $timeout = 30; // 默认锁超时时间30s

    // 锁的持有者
    protected $holder;

    // 获取锁的时间戳,用于超时释放锁
    protected $lockTime;

    function __construct(){
        $this->redis = new Redis();
        $this->redis->connect('127.0.0.1', 6379);
    }

    function getRedisClient(){
        return $this->redis;
    }

    function setLock($key = ''){
        if(empty($key)){
            $key = $this->lockKey;
        }else{
            $key = $this->lockKey . '_' . $key; // 如果有传入锁键名,则拼接上前缀方便管理
        }

        $timeout = $this->timeout;

        // 检查锁是否已被其他节点持有
        if($lockTime = $this->redis->get($key)){
            // 计算锁的剩余时间
            $remainTime = $lockTime + $timeout - time();

            // 如果锁剩余时间大于0,则表示其他节点持有锁
            if($remainTime > 0){
                return false;
            }
        }

        // 尝试获取锁
        $result = $this->redis->set($key, time() + $timeout, ['nx', 'ex' => $timeout]);

        if ($result){
            $this->holder = $key;
            $this->lockTime = time() + $timeout;

            return true;
        }

        return false;
    }

    function unlock($key = ''){
        if(empty($key)){
            $key = $this->lockKey;
        }else{
            $key = $this->lockKey . '_' . $key; // 如果有传入锁键名,则拼接上前缀方便管理
        }

        if($this->holder != $key){
            return false;
        }

        return $this->redis->del($key);
    }

    // 定时任务,每隔一段时间检查锁的持有者是否存活,如果不存活,则尝试切换为当前节点
    function timeoutReset(){
        while(true){
            if(time() > $this->lockTime){
                $this->redis->set($this->holder, '', ['nx', 'ex' => 1]);
            }

            $this->holder = $this->redis->get($this->lockKey);

            if(!$this->holder){
                $this->setLock();
            }

            sleep(1); // 每个一秒执行一次
        }
    }
}

上記は、インテリジェントなロック スイッチングを実装しています。ノード A に障害が発生した場合、ロックがタイムアウトになった後、他のノードが自動的にロック ホルダーに切り替わります。このメカニズムにより、分散ロックの高可用性と信頼性が保証されます。

4. 概要

この記事では、Redis を使用して分散ロックを実装し、インテリジェントなロック切り替えメカニズムを実装する方法を紹介します。このソリューションは、分散ロックの高可用性と信頼性を保証し、分散システムで広く使用できます。同時に、このソリューションは、データベースの読み取り/書き込みロック、分散トランザクションなど、ロック メカニズムを必要とする他の問題の解決にも使用できます。

以上がPHP で Redis を使用して分散ロックのインテリジェントなスイッチングを実装するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

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