激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

服務器之家:專注于服務器技術及軟件下載分享
分類導航

Mysql|Sql Server|Oracle|Redis|MongoDB|PostgreSQL|Sqlite|DB2|mariadb|Access|數據庫技術|

服務器之家 - 數據庫 - Redis - Redis上實現分布式鎖以提高性能的方案研究

Redis上實現分布式鎖以提高性能的方案研究

2019-10-27 16:38Kelly Redis

這篇文章主要介紹了Redis上實現分布式鎖以提高性能的方案研究,其中重點需要理解異步算法與鎖的自動釋放,需要的朋友可以參考下

背景:

在很多互聯網產品應用中,有些場景需要加鎖處理,比如:秒殺,全局遞增ID,樓層生成等等。大部分是解決方案基于DB實現的,Redis為單進程單線程模式,采用隊列模式將并發訪問變成串行訪問,且多客戶端對Redis的連接并不存在競爭關系。

項目實踐

任務隊列用到分布式鎖的情況比較多,在將業務邏輯中可以異步處理的操作放入隊列,在其他線程中處理后出隊,此時隊列中使用了分布式鎖,保證入隊和出隊的一致性。關于redis隊列這塊的邏輯分析,我將在下一次對其進行總結,此處先略過。

接下來對redis實現的分布式鎖的邏輯代碼進行詳細的分析和理解:

1、為避免特殊原因導致鎖無法釋放, 在加鎖成功后, 鎖會被賦予一個生存時間(通過 lock 方法的參數設置或者使用默認值), 超出生存時間鎖將被自動釋放.

2、鎖的生存時間默認比較短(秒級, 具體見 lock 方法), 因此若需要長時間加鎖, 可以通過 expire 方法延長鎖的生存時間為適當的時間. 比如在循環內調用 expire

3、系統級的鎖當進程無論因為任何原因出現crash,操作系統會自己回收鎖,所以不會出現資源丟失。

4、但分布式鎖不同。若一次性設置很長的時間,一旦由于各種原因進程 crash 或其他異常導致 unlock 未被調用,則該鎖在剩下的時間就變成了垃圾鎖,導致其他進程或進程重啟后無法進入加鎖區域。

  1. <?php 
  2.    
  3. require_once 'RedisFactory.php'
  4.    
  5. /** 
  6. * 在 Redis 上實現的分布式鎖 
  7. */ 
  8. class RedisLock { 
  9.     
  10. //單例模式 
  11.   private static $_instance = null
  12.   public static function instance() { 
  13.     if(self::$_instance == null) { 
  14.       self::$_instance = new RedisLock(); 
  15.     } 
  16.     return self::$_instance; 
  17.   } 
  18.    
  19.     
  20. //redis對象變量 
  21.   private $redis; 
  22.     
  23. //存放被鎖的標志名的數組 
  24.   private $lockedNames = array(); 
  25.    
  26.   public function __construct() { 
  27.       
  28. //獲取一個 RedisString 實例 
  29.     $this->redis = RedisFactory::instance()->getString(); 
  30.   } 
  31.    
  32.     
  33. /**  
  34.     
  35. * 加鎖 
  36.     
  37. * 
  38.     
  39. * @param string 鎖的標識名 
  40.     
  41. * @param int 獲取鎖失敗時的等待超時時間(秒), 在此時間之內會一直嘗試獲取鎖直到超時. 為 0 表示失敗后直接返回不等待 
  42.     
  43. * @param int 當前鎖的最大生存時間(秒), 必須大于 0 . 如果超過生存時間后鎖仍未被釋放, 則系統會自動將其強制釋放 
  44.     
  45. * @param int 獲取鎖失敗后掛起再試的時間間隔(微秒) 
  46.     
  47. */ 
  48.   public function lock($name, $timeout = 0, $expire = 15, $waitIntervalUs = 100000) { 
  49.     if(empty($name)) return false
  50.    
  51.     $timeout = (int)$timeout; 
  52.     $expire = max((int)$expire, 5); 
  53.     $now = microtime(true); 
  54.     $timeoutAt = $now + $timeout; 
  55.     $expireAt = $now + $expire; 
  56.    
  57.     $redisKey = "Lock:$name"
  58.     while(true) { 
  59.       $result = $this->redis->setnx($redisKey, (string)$expireAt); 
  60.       if($result !== false) { 
  61.           
  62. //對$redisKey設置生存時間 
  63.         $this->redis->expire($redisKey, $expire); 
  64.           
  65. //將最大生存時刻記錄在一個數組里面 
  66.         $this->lockedNames[$name] = $expireAt; 
  67.         return true
  68.       } 
  69.    
  70.         
  71. //以秒為單位,返回$redisKey 的剩余生存時間 
  72.       $ttl = $this->redis->ttl($redisKey); 
  73.         
  74. // TTL 小于 0 表示 key 上沒有設置生存時間(key 不會不存在, 因為前面 setnx 會自動創建) 
  75.         
  76. // 如果出現這種情況, 那就是進程在某個實例 setnx 成功后 crash 導致緊跟著的 expire 沒有被調用. 這時可以直接設置 expire 并把鎖納為己用 
  77.       if($ttl < 0) { 
  78.         $this->redis->set($redisKey, (string)$expireAt, $expire); 
  79.         $this->lockedNames[$name] = $expireAt; 
  80.         return true
  81.       } 
  82.    
  83.         
  84. // 設置了不等待或者已超時 
  85.       if($timeout <= 0 || microtime(true) > $timeoutAt) break
  86.    
  87.         
  88. // 掛起一段時間再試 
  89.       usleep($waitIntervalUs); 
  90.     } 
  91.    
  92.     return false
  93.   } 
  94.    
  95.     
  96. /** 
  97.     
  98. * 給當前鎖增加指定的生存時間(秒), 必須大于 0 
  99.     
  100. * 
  101.     
  102. * @param string 鎖的標識名 
  103.     
  104. * @param int 生存時間(秒), 必須大于 0 
  105.     
  106. */ 
  107.   public function expire($name, $expire) { 
  108.     if($this->isLocking($name)) { 
  109.       if($this->redis->expire("Lock:$name", max($expire, 1))) { 
  110.         return true
  111.       } 
  112.     } 
  113.     return false
  114.   } 
  115.    
  116.     
  117. /** 
  118.     
  119. * 判斷當前是否擁有指定名稱的鎖 
  120.     
  121. * 
  122.     
  123. * @param mixed $name 
  124.     
  125. */ 
  126.   public function isLocking($name) { 
  127.     if(isset($this->lockedNames[$name])) { 
  128.       return (string)$this->lockedNames[$name] == (string)$this->redis->get("Lock:$name"); 
  129.     } 
  130.     return false
  131.   } 
  132.    
  133.     
  134. /** 
  135.     
  136. * 釋放鎖 
  137.     
  138. * 
  139.     
  140. * @param string 鎖的標識名 
  141.     
  142. */ 
  143.   public function unlock($name) { 
  144.     if($this->isLocking($name)) { 
  145.       if($this->redis->deleteKey("Lock:$name")) { 
  146.         unset($this->lockedNames[$name]); 
  147.         return true
  148.       } 
  149.     } 
  150.     return false
  151.   } 
  152.    
  153.     
  154. /** 釋放當前已經獲取到的所有鎖 */ 
  155.   public function unlockAll() { 
  156.     $allSuccess = true
  157.     foreach($this->lockedNames as $name => $item) { 
  158.       if(false === $this->unlock($name)) { 
  159.         $allSuccess = false
  160.       } 
  161.     } 
  162.     return $allSuccess; 
  163.   } 

此類很多代碼都寫上了注釋,只要認真理解下,就很容易懂得如何在redis實現分布式鎖了。

延伸 · 閱讀

精彩推薦
  • Redisredis 交集、并集、差集的具體使用

    redis 交集、并集、差集的具體使用

    這篇文章主要介紹了redis 交集、并集、差集的具體使用,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友...

    xiaojin21cen10152021-07-27
  • RedisRedis 事務知識點相關總結

    Redis 事務知識點相關總結

    這篇文章主要介紹了Redis 事務相關總結,幫助大家更好的理解和學習使用Redis,感興趣的朋友可以了解下...

    AsiaYe8232021-07-28
  • Redisredis中如何使用lua腳本讓你的靈活性提高5個逼格詳解

    redis中如何使用lua腳本讓你的靈活性提高5個逼格詳解

    這篇文章主要給大家介紹了關于redis中如何使用lua腳本讓你的靈活性提高5個逼格的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具...

    一線碼農5812019-11-18
  • Redisredis實現排行榜功能

    redis實現排行榜功能

    排行榜在很多地方都能使用到,redis的zset可以很方便地用來實現排行榜功能,本文就來簡單的介紹一下如何使用,具有一定的參考價值,感興趣的小伙伴們...

    乘月歸5022021-08-05
  • RedisRedis的配置、啟動、操作和關閉方法

    Redis的配置、啟動、操作和關閉方法

    今天小編就為大家分享一篇Redis的配置、啟動、操作和關閉方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧 ...

    大道化簡5312019-11-14
  • Redis詳解Redis復制原理

    詳解Redis復制原理

    與大多數db一樣,Redis也提供了復制機制,以滿足故障恢復和負載均衡等需求。復制也是Redis高可用的基礎,哨兵和集群都是建立在復制基礎上實現高可用的...

    李留廣10222021-08-09
  • RedisRedis全量復制與部分復制示例詳解

    Redis全量復制與部分復制示例詳解

    這篇文章主要給大家介紹了關于Redis全量復制與部分復制的相關資料,文中通過示例代碼介紹的非常詳細,對大家學習或者使用Redis爬蟲具有一定的參考學習...

    豆子先生5052019-11-27
  • RedisRedis如何實現數據庫讀寫分離詳解

    Redis如何實現數據庫讀寫分離詳解

    Redis的主從架構,能幫助我們實現讀多,寫少的情況,下面這篇文章主要給大家介紹了關于Redis如何實現數據庫讀寫分離的相關資料,文中通過示例代碼介紹...

    羅兵漂流記6092019-11-11
主站蜘蛛池模板: 黄网站免费在线看 | 欧洲黄视频 | 日日摸夜夜骑 | 久久综合综合久久 | 88xx成人精品视频 | 悠悠成人资源亚洲一区二区 | 美国人成人在线视频 | 久久千人斩 | 中文字幕免费在线观看视频 | 成人爱情偷拍视频在线观看 | 国产精品一区二区三区在线播放 | 亚洲电影在线观看高清免费 | 国产成年人在线观看 | 色天天综合网 | 老司机免费福利午夜入口ae58 | www亚洲免费 | 国产精彩视频在线 | 91丨九色丨国产在线观看 | 欧美成人精品不卡视频在线观看 | 黄色成人小视频 | 久久久久国产成人精品亚洲午夜 | 成人在线视频精品 | 亚洲精品永久视频 | 一区二区久久久久草草 | 斗罗破苍穹在线观看免费完整观看 | 久久欧美亚洲另类专区91大神 | 国产中文99视频在线观看 | 国产精品久久久久久久av | 国产精品久久久久久模特 | 中文字幕精品在线播放 | 黄色片免费看网站 | 欧产日产国产精品99 | 麻豆传传媒久久久爱 | 狠狠操电影 | 99视频网址 | 日本一区免费看 | 亚洲欧美日韩精品久久 | 一区二区三区四区高清视频 | 国产精品91久久久 | 99在线啪 | xxxxhd73国产 |