《Redis 分佈式鎖》

一:什麼是分佈式鎖。php

  -  通俗來講的話,就是在分佈式架構的redis中,使用鎖redis

 

二:分佈式鎖的使用選擇。算法

  - 當 Redis 的使用場景很少,並且也只是單個在用的時候,能夠構建本身使用的 鎖。架構

  - 若是在公司裏落地生產環境用分佈式鎖的時候,通常是會用開源類庫。併發

  - Redis分佈式鎖,通常就是用 Redisson 框架就行了,很是的簡便易用。 框架

 

三:Redisson 實現Redis分佈式鎖的原理。異步

  - 總體流程圖分佈式

    - this

 

  - 詳解spa

    - 當某個請求的客戶端須要加鎖時候。

      - 若是該客戶端面對的是一個redis cluster集羣,他首先會根據hash算法選擇一臺機器。(一致hash算法)

 

    - 隨後會向Redis發送加鎖請求(原理爲Lua腳本

      - Lua 腳本會保證業務執行的原子性,因此採用 Lua 的方式爲加鎖命令。

      - 

      - 這個Lua 腳本的大體意思爲(這個Key的鎖是否存在,若是存在,則一直循環等待,如不存在,則上鎖,並設置過時時間。

 

    - watch dog 提供的鎖自動延期的能力

      - 加鎖的鎖key默認生存時間才30秒,若是超過了30秒,客戶端1還想一直持有這把鎖,怎麼辦呢?

      - RedisSon 只要加鎖成功,就會啓動一個watch dog看門狗, 他是一個後臺線程,會每隔10秒檢查一下 ,若是還持有鎖key,那麼就會不斷的延長鎖key的生存時間。

 

四:分佈式鎖鎖帶來的問題

  - 在分佈式架構中的問題,就是若是你對某個redis master實例,寫入了key的鎖,此時會異步複製給對應的master slave實例。

  - 可是這個過程當中一旦發生redis master宕機,主備切換,redis slave變爲了redis master。

  - 接着就會致使,客戶端2來嘗試加鎖的時候,在新的redis master上完成了加鎖,而客戶端1也覺得本身成功加了鎖。

  - 此時就會致使多個客戶端對一個分佈式鎖完成了加鎖。

  - 這時系統在業務語義上必定會出現問題, 致使各類髒數據的產生 。

  - 這個就是redis cluster/redis master-slave架構的 主從異步複製 致使的redis分佈式鎖的問題:在redis master實例宕機的時候,可能致使多個客戶端同時完成加鎖。

 

五:簡單實現的redis鎖

  - 原理

    -  經過 redis 的 setnx 功能進行加鎖

 

  - 實現(PHP)

    • <?php
      /**
       * 建立 Redis 單例
       * @return mixed
       */
      class redisInstance { static $redis; private function __construct() { } public static function getInstance() { if (self::$redis) { return self::$redis; } self::$redis = new \Redis(); return self::$redis; } } class redisLock { public $reids; public function __construct() { $this->reids = redisInstance::getInstance(); } /** * 鍵加鎖,默認加鎖時間爲 1分鐘 = 60 * 1000(毫秒) * @param $key * @param int $ttl * @return bool */ public function lock($key, $ttl = 60000) { $lockKey = $key . '_lock'; // 經過setNx命令拿到鎖 $lock = $this->reids->set($lockKey, 1, ['NX', 'PX' => $ttl]); // 拿到鎖則直接返回 if ($lock) { return true; } // 沒有拿到鎖,則一直循環等待鎖資源釋放 while (!$lock) { $lock = $this->reids->set($key, 1, ['NX', 'PX' => $ttl]); } return $lock; } /** * 釋放鎖 * @param $key * @return bool */ public function unLock($key) { $lockKey = $key . '_lock'; $lock = false; // 釋放鎖 while (!$lock) { $lock = $this->reids->del($lockKey); } return true; } } $redis = redisInstance::getInstance(); $redisLock = new redisLock(); /** * 例如,買商品,進行庫存遞減 * 1:對庫存加鎖 * 2:遞減 * 3:釋放鎖 * 影響 * 加鎖致使的併發度下降 */ $key = 'stock'; $redisLock->lock($key); $stock = $redis->get($key); if ($redis->get($key) <= 0) { $redisLock->unLock($key); return false; } $redis->decr($key); $redisLock->unLock($key);
相關文章
相關標籤/搜索