日常開發中我們少不了要用分布式鎖,保證資源訪問的互斥,下面將一步步給出最佳的解決方案:
1、redis2.6以上可以用lua指令碼實現加鎖和設定有效期的原子性操作,但是redis2.6以下不支援lua指令碼,只能採取get+setex或setnx+expire兩種方案,前者忽略掉了get和setex之間的併發,而後者是忽視了ex失敗的問題。
2、可以公升級到新版本,支援set同時設定nx和ex引數,實現如下(加鎖失敗的自旋沒有實現,可根據業務自行實現):
<?php
$redis = new redis();
$redis->connect('127.0.0.1', 6379);
$key = 'key:1';
$random = random(); //自定義的隨機值函式
$ok = $redis->set($key, 1, ['nx', 'ex'=>$expire])
if( $ok )
}?>
3、上面的鎖還有問題,假如在獲取對比隨機值和刪除之間宕機了,就只能等待鎖自動失效。並且在高併發場景下,應該儘量減少redis命令的次數,所以在釋放鎖時,我們可以用lua實現原子命令,改動後的**如下:
<?php
$redis = new redis();
$redis->connect('127.0.0.1', 6379);
$key = 'key:1';
$expire = 10;
$ok = $redis->set($key, $random, ['nx', 'ex' => $expire]);
if ($ok)
127.0.0.1:6379> monitor
1594377461.656859 [0 127.0.0.1:58550] "set" "key:1" "5f0844f5a0357" "ex" "10" "nx"
1594377466.663522 [0 127.0.0.1:58550] "eval" "\n if redis.call(\"get\", keys[1]) == ar**[1] then\n return redis.call(\"del\", keys[1])\n else\n return 0\n end\n " "1" "key:1" "5f0844f5a0357"
1594377466.663617 [0 lua] "get" "key:1"
1594377466.663646 [0 lua] "del" "key:1"
4、上面的**你以為就萬無一失了?假如我們的redis伺服器宕機了怎麼辦,這時有人可能會脫口而出,主從複製+哨兵啊,仔細想想此時會不會出現新問題?
肯定是會的,a執行緒在master加鎖後,假如在鎖的有效期內,主節點宕機,從節點公升級為主節點,此時b執行緒又來獲取鎖,這時新的主節點是沒有鎖的,就會導致b執行緒加鎖成功並獲取資源,這就出現了鎖競態。那麼這個問題如何解決呢?redis的開發者已經給我們提供了現成的解決方法:redlock。
核心思想:使用多個redis主節點(要大於等於3個),加鎖時所有節點都加鎖,只有半數以上的節點加鎖成功才算成功,釋放鎖時所有節點都釋放。
redlock**:
分布式鎖 使用Redis實現分布式鎖
關於分布式鎖的實現,我的前一篇文章講解了如何使用zookeeper實現分布式鎖。關於分布式鎖的背景此處不再做贅述,我們直接討論下如何使用redis實現分布式鎖。關於redis,筆主不打算做長篇大論的介紹,只介紹下redis優秀的特性。支援豐富的資料型別,如string list map set zs...
redis實現分布式鎖
隨便 系統越來越大,各功能模組除了垂直切割以外,同時也得做集群處理,那麼問題來了,在多執行緒情況下對於資源的競爭就需要乙個統一的訪問限制。以選課系統為例子,集群中各節點對課程可選數量同時操作,這裡就需要同步了,否則會導致最後選到的數量比可選的數量大,這裡我們的分布式鎖就派上用場了。利用redis來實...
redis實現分布式鎖
分布式鎖可以基於很多種方式實現,比如zookeeper redis.不管哪種方式,他的 基本原理是不變的 用乙個狀態值表示鎖,對鎖的占用和釋放通過狀態值來標識。1 使用redis的setnx命令實現分布式鎖 1 實現的原理 redis為單程序單執行緒模式,採用佇列模式將併發訪問變成序列訪問,且多客戶...