基於Redis實現分布式鎖之前,這些坑你一定得知道

2020-09-03 程式設計師道道

開頭

基於Redis的分布式鎖對大家來說並不陌生,可是你的分布式鎖有失敗的時候嗎?在失敗的時候可曾懷疑過你在用的分布式鎖真的靠譜嗎?以下是結合自己的踩坑經驗總結的一些經驗之談。

你真的需要分布式鎖嗎?

用到分布式鎖說明遇到了多個進程共同訪問同一個資源的問題, 一般是在兩個場景下會防止對同一個資源的重複訪問:

  1. 提高效率。比如多個節點計算同一批任務,如果某個任務已經有節點在計算了,那其他節點就不用重複計算了,以免浪費計算資源。不過重複計算也沒事,不會造成其他更大的損失。也就是允許偶爾的失敗。
  2. 保證正確性。這種情況對鎖的要求就很高了,如果重複計算,會對正確性造成影響。這種不允許失敗。

引入分布式鎖勢必要引入一個第三方的基礎設施,比如MySQL,Redis,zookeeper等,這些實現分布式鎖的基礎設施出問題了,也會影響業務,所以在使用分布式鎖前可以考慮下是否可以不用加鎖的方式實現?不過這個不在本文的討論範圍內,本文假設加鎖的需求是合理的,並且偏向於上面的第二種情況,為什麼是偏向?因為不存在100%靠譜的分布式鎖,看完下面的內容就明白了。

從一個簡單的分布式鎖實現說起

分布式鎖的Redis實現很常見,自己實現和使用第三方庫都很簡單,至少看上去是這樣的,這裡就介紹一個最簡單靠譜的Redis實現。

最簡單的實現

實現很經典了,這裡只提兩個要點?

  1. 加鎖和解鎖的鎖必須是同一個,常見的解決方案是給每個鎖一個鑰匙(唯一ID),加鎖時生成,解鎖時判斷。
  2. 不能讓一個資源永久加鎖。常見的解決方案是給一個鎖的過期時間。當然了還有其他方案,後面再說。

一個可複製粘貼的實現方式如下:

加鎖

public static boolean tryLock(String key, String uniqueId, int seconds) { return &34;.equals(jedis.set(key, uniqueId, &34;, &34;, seconds));}

這裡其實是調用了 SET key value PX milliseoncds NX,不明白這個命令的參考下SET key value [EX seconds|PX milliseconds] [NX|XX] [KEEPTTL]

解鎖

public static boolean releaseLock(String key, String uniqueId) { String luaScript = &39;get&34; + &39;del&34;; return jedis.eval( luaScript, Collections.singletonList(key), Collections.singletonList(uniqueId) ).equals(1L);}

這段實現的精髓在那個簡單的lua腳本上,先判斷唯一ID是否相等再操作。

靠譜嗎?

這樣的實現有什麼問題呢?

  1. 單點問題。上面的實現只要一個master節點就能搞定,這裡的單點指的是單master,就算是個集群,如果加鎖成功後,鎖從master複製到slave的時候掛了,也是會出現同一資源被多個client加鎖的。
  2. 執行時間超過了鎖的過期時間。上面寫到為了不出現一直上鎖的情況,加了一個兜底的過期時間,時間到了鎖自動釋放,但是,如果在這期間任務並沒有做完怎麼辦?由於GC或者網絡延遲導致的任務時間變長,很難保證任務一定能在鎖的過期時間內完成。

如何解決這兩個問題呢?試試看更複雜的實現吧。

Redlock算法

對於第一個單點問題,順著redis的思路,接下來想到的肯定是Redlock了。Redlock為了解決單機的問題,需要多個(大於2)redis的master節點,多個master節點互相獨立,沒有數據同步。

Redlock的實現如下:

  1. 獲取當前時間。
  2. 依次獲取N個節點的鎖。 每個節點加鎖的實現方式同上。這裡有個細節,就是每次獲取鎖的時候的過期時間都不同,需要減去之前獲取鎖的操作的耗時,
  • 比如傳入的鎖的過期時間為500ms,
  • 獲取第一個節點的鎖花了1ms,那麼第一個節點的鎖的過期時間就是499ms,
  • 獲取第二個節點的鎖花了2ms,那麼第二個節點的鎖的過期時間就是497ms
  • 如果鎖的過期時間小於等於0了,說明整個獲取鎖的操作超時了,整個操作失敗
  1. 判斷是否獲取鎖成功。 如果client在上述步驟中獲取到了(N/2 + 1)個節點鎖,並且每個鎖的過期時間都是大於0的,則獲取鎖成功,否則失敗。失敗時釋放鎖。
  2. 釋放鎖。 對所有節點發送釋放鎖的指令,每個節點的實現邏輯和上面的簡單實現一樣。為什麼要對所有節點操作?因為分布式場景下從一個節點獲取鎖失敗不代表在那個節點上加速失敗,可能實際上加鎖已經成功了,但是返回時因為網絡抖動超時了。

以上就是大家常見的redlock實現的描述了,一眼看上去就是簡單版本的多master版本,如果真是這樣就太簡單了,接下來分析下這個算法在各個場景下是怎樣被玩壞的。

分布式鎖的坑

高並發場景下的問題

以下問題不是說在並發不高的場景下不容易出現,只是在高並發場景下出現的概率更高些而已。 性能問題。 性能問題來自於兩個方面。

  1. 獲取鎖的時間上。如果redlock運用在高並發的場景下,存在N個master節點,一個一個去請求,耗時會比較長,從而影響性能。這個好解決。通過上面描述不難發現,從多個節點獲取鎖的操作並不是一個同步操作,可以是異步操作,這樣可以多個節點同時獲取。即使是並行處理的,還是得預估好獲取鎖的時間,保證鎖的TTL > 獲取鎖的時間+任務處理時間。
  2. 被加鎖的資源太大。加鎖的方案本身就是會為了正確性而犧牲並發的,犧牲和資源大小成正比。這個時候可以考慮對資源做拆分,拆分的方式有兩種:
  3. 從業務上將鎖住的資源拆分成多段,每段分開加鎖。比如,我要對一個商戶做若干個操作,操作前要鎖住這個商戶,這時我可以將若干個操作拆成多個獨立的步驟分開加鎖,提高並發。
  4. 用分桶的思想,將一個資源拆分成多個桶,一個加鎖失敗立即嘗試下一個。比如批量任務處理的場景,要處理200w個商戶的任務,為了提高處理速度,用多個線程,每個線程取100個商戶處理,就得給這100個商戶加鎖,如果不加處理,很難保證同一時刻兩個線程加鎖的商戶沒有重疊,這時可以按一個維度,比如某個標籤,對商戶進行分桶,然後一個任務處理一個分桶,處理完這個分桶再處理下一個分桶,減少競爭。

重試的問題。 無論是簡單實現還是redlock實現,都會有重試的邏輯。如果直接按上面的算法實現,是會存在多個client幾乎在同一時刻獲取同一個鎖,然後每個client都鎖住了部分節點,但是沒有一個client獲取大多數節點的情況。解決的方案也很常見,在重試的時候讓多個節點錯開,錯開的方式就是在重試時間中加一個隨機時間。這樣並不能根治這個問題,但是可以有效緩解問題,親試有效。

節點宕機

對於單master節點且沒有做持久化的場景,宕機就掛了,這個就必須在實現上支持重複操作,自己做好冪等。

對於多master的場景,比如redlock,我們來看這樣一個場景:

  1. 假設有5個redis的節點:A、B、C、D、E,沒有做持久化。
  2. client1從A、B、C 3個節點獲取鎖成功,那麼client1獲取鎖成功。
  3. 節點C掛了。
  4. client2從C、D、E獲取鎖成功,client2也獲取鎖成功,那麼在同一時刻client1和client2同時獲取鎖,redlock被玩壞了。

怎麼解決呢?最容易想到的方案是打開持久化。持久化可以做到持久化每一條redis命令,但這對性能影響會很大,一般不會採用,如果不採用這種方式,在節點掛的時候肯定會損失小部分的數據,可能我們的鎖就在其中。 另一個方案是延遲啟動。就是一個節點掛了修復後,不立即加入,而是等待一段時間再加入,等待時間要大於宕機那一刻所有鎖的最大TTL。 但這個方案依然不能解決問題,如果在上述步驟3中B和C都掛了呢,那麼只剩A、D、E三個節點,從D和E獲取鎖成功就可以了,還是會出問題。那麼只能增加master節點的總量,緩解這個問題了。增加master節點會提高穩定性,但是也增加了成本,需要在兩者之間權衡。

任務執行時間超過鎖的TTL

之前產線上出現過因為網絡延遲導致任務的執行時間遠超預期,鎖過期,被多個線程執行的情況。 這個問題是所有分布式鎖都要面臨的問題,包括基於zookeeper和DB實現的分布式鎖,這是鎖過期了和client不知道鎖過期了之間的矛盾。 在加鎖的時候,我們一般都會給一個鎖的TTL,這是為了防止加鎖後client宕機,鎖無法被釋放的問題。但是所有這種姿勢的用法都會面臨同一個問題,就是沒發保證client的執行時間一定小於鎖的TTL。雖然大多數程式設計師都會樂觀的認為這種情況不可能發生,我也曾經這麼認為,直到被現實一次又一次的打臉。

Martin Kleppmann也質疑過這一點,這裡直接用他的圖:

  1. Client1獲取到鎖
  2. Client1開始任務,然後發生了STW的GC,時間超過了鎖的過期時間
  3. Client2 獲取到鎖,開始了任務
  4. Client1的GC結束,繼續任務,這個時候Client1和Client2都認為自己獲取了鎖,都會處理任務,從而發生錯誤。

Martin Kleppmann舉的是GC的例子,我碰到的是網絡延遲的情況。不管是哪種情況,不可否認的是這種情況無法避免,一旦出現很容易懵逼。

如何解決呢?一種解決方案是不設置TTL,而是在獲取鎖成功後,給鎖加一個watchdog,watchdog會起一個定時任務,在鎖沒有被釋放且快要過期的時候會續期。這樣說有些抽象,下面結合redisson源碼說下:

public class RedissonLock extends RedissonExpirable implements RLock { ... @Override public void lock() { try { lockInterruptibly(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } @Override public void lock(long leaseTime, TimeUnit unit) { try { lockInterruptibly(leaseTime, unit); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } ... }

redisson常用的加鎖api是上面兩個,一個是不傳入TTL,這時是redisson自己維護,會主動續期;另外一種是自己傳入TTL,這種redisson就不會幫我們自動續期了,或者自己將leaseTime的值傳成-1,但是不建議這種方式,既然已經有現成的API了,何必還要用這種奇怪的寫法呢。 接下來分析下不傳參的方法的加鎖邏輯:

public class RedissonLock extends RedissonExpirable implements RLock { ... public static final long LOCK_EXPIRATION_INTERVAL_SECONDS = 30; protected long internalLockLeaseTime = TimeUnit.SECONDS.toMillis(LOCK_EXPIRATION_INTERVAL_SECONDS); @Override public void lock() { try { lockInterruptibly(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } @Override public void lockInterruptibly() throws InterruptedException { lockInterruptibly(-1, null); } @Override public void lockInterruptibly(long leaseTime, TimeUnit unit) throws InterruptedException { long threadId = Thread.currentThread().getId(); Long ttl = tryAcquire(leaseTime, unit, threadId); // lock acquired if (ttl == null) { return; } RFuture<RedissonLockEntry> future = subscribe(threadId); commandExecutor.syncSubscription(future); try { while (true) { ttl = tryAcquire(leaseTime, unit, threadId); // lock acquired if (ttl == null) { break; } // waiting for message if (ttl >= 0) { getEntry(threadId).getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS); } else { getEntry(threadId).getLatch().acquire(); } } } finally { unsubscribe(future, threadId); }// get(lockAsync(leaseTime, unit)); } private Long tryAcquire(long leaseTime, TimeUnit unit, long threadId) { return get(tryAcquireAsync(leaseTime, unit, threadId)); } private <T> RFuture<Long> tryAcquireAsync(long leaseTime, TimeUnit unit, final long threadId) { if (leaseTime != -1) { return tryLockInnerAsync(leaseTime, unit, threadId, RedisCommands.EVAL_LONG); } RFuture<Long> ttlRemainingFuture = tryLockInnerAsync(LOCK_EXPIRATION_INTERVAL_SECONDS, TimeUnit.SECONDS, threadId, RedisCommands.EVAL_LONG); ttlRemainingFuture.addListener(new FutureListener<Long>() { @Override public void operationComplete(Future<Long> future) throws Exception { if (!future.isSuccess()) { return; } Long ttlRemaining = future.getNow(); // lock acquired if (ttlRemaining == null) { scheduleExpirationRenewal(threadId); } } }); return ttlRemainingFuture; } private void scheduleExpirationRenewal(final long threadId) { if (expirationRenewalMap.containsKey(getEntryName())) { return; } Timeout task = commandExecutor.getConnectionManager().newTimeout(new TimerTask() { @Override public void run(Timeout timeout) throws Exception { RFuture<Boolean> future = commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN, &39;hexists&34; + &39;pexpire&34; + &34; + &34; + &34;, Collections.<Object>singletonList(getName()), internalLockLeaseTime, getLockName(threadId)); future.addListener(new FutureListener<Boolean>() { @Override public void operationComplete(Future<Boolean> future) throws Exception { expirationRenewalMap.remove(getEntryName()); if (!future.isSuccess()) { log.error(&39;t update lock &34; expiration&tryAcquireAsync中,在獲取鎖成功後,會進入scheduleExpirationRenewal,這裡面初始化了一個定時器,dely的時間是internalLockLeaseTime / 3。在redisson中,internalLockLeaseTime是30s,也就是每隔10s續期一次,每次30s。 如果是基於zookeeper實現的分布式鎖,可以利用zookeeper檢查節點是否存活,從而實現續期,zookeeper分布式鎖沒用過,不詳細說。

不過這種做法也無法百分百做到同一時刻只有一個client獲取到鎖,如果續期失敗,比如發生了Martin Kleppmann所說的STW的GC,或者client和redis集群失聯了,只要續期失敗,就會造成同一時刻有多個client獲得鎖了。在我的場景下,我將鎖的粒度拆小了,redisson的續期機制已經夠用了。 如果要做得更嚴格,得加一個續期失敗終止任務的邏輯。這種做法在以前Python的代碼中實現過,Java還沒有碰到這麼嚴格的情況。

這裡也提下Martin Kleppmann的解決方案,我自己覺得這個方案並不靠譜,原因後面會提到。 他的方案是讓加鎖的資源自己維護一套保證不會因加鎖失敗而導致多個client在同一時刻訪問同一個資源的情況。



在客戶端獲取鎖的同時,也獲取到一個資源的token,這個token是單調遞增的,每次在寫資源時,都檢查當前的token是否是較老的token,如果是就不讓寫。對於上面的場景,Client1獲取鎖的同時分配一個33的token,Client2獲取鎖的時候分配一個34的token,在client1 GC期間,Client2已經寫了資源,這時最大的token就是34了,client1 從GC中回來,再帶著33的token寫資源時,會因為token過期被拒絕。這種做法需要資源那一邊提供一個token生成器。 對於這種fencing的方案,我有幾點問題:


  1. 無法保證事務。示意圖中畫的只有34訪問了storage,但是在實際場景中,可能出現在一個任務內多次訪問storage的情況,而且必須是原子的。如果client1帶著33token在GC前訪問過一次storage,然後發生了GC。client2獲取到鎖,帶著34的token也訪問了storage,這時兩個client寫入的數據是否還能保證數據正確?如果不能,那麼這種方案就有缺陷,除非storage自己有其他機制可以保證,比如事務機制;如果能,那麼這裡的token就是多餘的,fencing的方案就是多此一舉。
  2. 高並發場景不實用。因為每次只有最大的token能寫,這樣storage的訪問就是線性的,在高並發場景下,這種方式會極大的限制吞吐量,而分布式鎖也大多是在這種場景下用的,很矛盾的設計。
  3. 這是所有分布式鎖的問題。這個方案是一個通用的方案,可以和Redlock用,也可以和其他的lock用。所以我理解僅僅是一個和Redlock無關的解決方案。

系統時鐘漂移

這個問題只是考慮過,但在實際項目中並沒有碰到過,因為理論上是可能出現的,這裡也說下。 redis的過期時間是依賴系統時鐘的,如果時鐘漂移過大時會影響到過期時間的計算。

為什麼系統時鐘會存在漂移呢?先簡單說下系統時間,linux提供了兩個系統時間:clock realtime和clock monotonic。clock realtime也就是xtime/wall time,這個時間時可以被用戶改變的,被NTP改變,gettimeofday拿的就是這個時間,redis的過期計算用的也是這個時間。 clock monotonic ,直譯過來時單調時間,不會被用戶改變,但是會被NTP改變。

最理想的情況時,所有系統的時鐘都時時刻刻和NTP伺服器保持同步,但這顯然時不可能的。導致系統時鐘漂移的原因有兩個:

  1. 系統的時鐘和NTP伺服器不同步。這個目前沒有特別好的解決方案,只能相信運維同學了。
  2. clock realtime被人為修改。在實現分布式鎖時,不要使用clock realtime。不過很可惜,redis使用的就是這個時間,我看了下Redis 5.0源碼,使用的還是clock realtime。Antirez說過改成clock monotonic的,不過大佬還沒有改。也就是說,人為修改redis伺服器的時間,就能讓redis出問題了。

總結

本文從一個簡單的基於redis的分布式鎖出發,到更複雜的Redlock的實現,介紹了在使用分布式鎖的過程中才踩過的一些坑以及解決方案。

相關焦點

  • Spring Cloud基於Redis實現的分布式鎖
    基於Redis實現的分布式鎖Spring Cloud 分布式環境下,同一個服務都是部署在不同的機器上,這種情況無法像單體架構下數據一致性問題採用加鎖就實現數據一致性問題,在高並發情況下,對於分布式架構顯然是不合適的,針對這種情況我們就需要用到分布式鎖了。
  • 基於 Redis 實現的分布式鎖
    基於資料庫實現分布式鎖;基於緩存(Redis等)實現分布式鎖;基於Zookeeper實現分布式鎖;3、採用 Redis 實現分布式鎖(直接上代碼)3.1、先定義註解 RedisLock* 獲取分布式鎖,默認: * 1、獲取鎖失敗時自旋嘗試獲取,默認自旋5次,每次間隔30毫秒 * * @param key 分布式鎖,redis key * @param expireTime 分布式鎖過期時間 * @param expireTimeUnit 分布式鎖過期時間單位 * @return * @author
  • java 從零實現屬於你的 redis 分布式鎖
    可重入鎖等等,這些鎖為我們變成提供極大的便利性,保證在多線程的情況下,保證線程安全。晚上關於 redis 分布式鎖的文章一大堆,但是也都稂莠不齊。redis 分布式鎖工具有時候中間件團隊不見得會提供,提供了也不見得經常維護,不如自己實現一個,知道原理,也方便修改。
  • 基於redis實現分布式鎖
    背景基於redis實現。使用主要是兩步1.獲取鎖2.釋放鎖代碼try{  獲取鎖;  處理業務;} finally{  釋放鎖;}>核心是基於redis的set命令。
  • Go和Redis實現分布式鎖
    本文介紹分布式鎖的問題,以及分布式鎖實現的方法。為什麼要使用鎖,問題的引入?在 一文中我們介紹了進程和線程,從文章中能了解到線程共享進程的內存全局變量,那麼對於全局變量數據一致性的要求,需要在進程內對修改行為加鎖以創造臨界區。
  • 利用Redis實現分布式鎖
    為什麼需要分布式鎖?在傳統單體應用單機部署的情況下,可以使用Java並發相關的鎖,如ReentrantLcok或synchronized進行互斥控制。但是,隨著業務發展的需要,原單體單機部署的系統,漸漸的被部署在多機器多JVM上同時提供服務,這使得原單機部署情況下的並發控制鎖策略失效了,為了解決這個問題就需要一種跨JVM的互斥機制來控制共享資源的訪問,這就是分布式鎖要解決的問題。
  • 聊聊Redis分布式鎖
    一、基於Redis分布式鎖方案1、Redis天然適合做分布式鎖,因為它是單進程單線程的,你所有的請求到Redis裡面,它都會進行串行化處理;2、實現方式Redis Setnx (SET if Not exists)命令在指定的key不存在時,為key設置指定的值① SETNX KEY_NAME VALUE Expire_Time --這是它獲取鎖的一個命令格式
  • 基於 Redis 的高容錯性分布式鎖架構設計方案 -01
    儘管選擇Zookeeper,甚至是基於RDBMS也能夠快速實現一個滿足業務場景的分布式鎖,但為何我們卻偏偏執著於Redis呢?簡而言之,選擇Redis主要是因為如下3個原因:1、高性能;2、原子操作;3、實現方便,成本低;雖然使用Redis提供的SETNX命令就可以很輕鬆的實現一個分布式鎖效果,但這樣的做法卻存在很大問題。
  • 分布式鎖沒那麼難,手把手教你實現 Redis 分布鎖!|保姆級教程
    雖然用不了 redisson,但是我可以研究其源碼,最後實現的可重入分布鎖參考了 redisson 實現方式。分布式鎖分布式鎖特性就要在於排他性,同一時間內多個調用方加鎖競爭,只能有一個調用方加鎖成功。
  • 我猜你還沒明白如何利用好Redis使用實現分布式鎖?
    1.1舉一個很長的例子系統 A 是一個電商系統,目前是一臺機器部署,系統中有一個用戶下訂單的接口,但是用戶下訂單之前一定要去檢查一下庫存,確保庫存足夠了才會給用戶下單。3.基於Redis實現分布式鎖3.1 使用Redis命令實現分布式鎖3.1.1加鎖加鎖實際上就是在redis中,給Key鍵設置一個值
  • 該如何一步步構建一個基於Redis的分布式鎖?
    一、介紹講介紹如何一步步構建一個基於Redis的分布式鎖。會從最原始的版本開始,然後根據問題進行調整,最後完成一個較為合理的分布式鎖。本篇文章會將分布式鎖的實現分為兩部分,一個是單機環境,另一個是集群環境下的Redis鎖實現。在介紹分布式鎖的實現之前,先來了解下分布式鎖的一些信息。
  • 經常用Redis,這些坑你知道嗎?
    分布式鎖 以我們之前做過的5人拼團為例。如果有用戶參加團購,我們需要先校驗參團人數是否達到了上限5人。如果沒達到5人,用戶才可以參團。接下來我們看看基於Redis分布式鎖的實現,以及特別要注意的問題。一般我們會基於setnx實現Redis分布式鎖。setnx命令可以檢查key是否存在,如果key不存在,就在Redis中創建一個鍵值對(操作成功),如果key已經存在就放棄執行(操作失敗)。
  • 分布式鎖解決方案-Redis
    ## 常見分布式鎖的實現分類### 1、基於資料庫的實現方式核心思想是:在資料庫中創建一個表,表中包含**方法名**等欄位,並在**方法名欄位上創建唯一索引**,想要執行某個方法,就使用這個方法名向表中插入數據,成功插入則獲取鎖,執行完成後刪除對應的行數據釋放鎖。
  • 實現一個Redis分布式鎖
    這一波操作明顯不符合原子性,如果代碼塊不加鎖,很容易因為並發導致超賣問題。咱們的系統如果是單體架構,那我們使用本地鎖就可以解決問題。如果是分布式架構,就需要使用分布式鎖。,雖然其他功能還未實現,但是應該不會造成死鎖。
  • 你之前看的都是錯的!看我怎麼使用Redis實現可靠的分布式鎖
    本文共4372字,將介紹如何正確的使用Redis來實現分布式鎖,並列舉目前常見的兩種錯誤的Redis分布式鎖實現方式,希望能給大家帶來幫助。關於使用Redis實現分布式鎖,在網絡上已經有很多的例子和文章,但大多數都是錯誤的。
  • 一起來學習分布式鎖
    為什麼要用分布式鎖我們先來看一個業務場景:系統 A 是一個電商系統,目前是一臺機器部署,系統中有一個用戶下訂單的接口,但是用戶下訂單之前一定要去檢查一下庫存,確保庫存那麼,如何實現分布式鎖呢?接著往下看!基於 Redis 實現分布式鎖上面分析為啥要使用分布式鎖了,這裡我們來具體看看分布式鎖落地的時候應該怎麼樣處理。
  • Redis實現分布式阻塞隊列
    Redis分布式鎖實現原理分布式鎖本質上要實現的目標就是在 Redis 裡面佔一個佔坑一般是使用 setnx(set if not exists) 指令,只允許被一個客戶端佔坑。先來先佔, 用完了,再調用 del 指令釋放茅坑。
  • 手撕redis鎖,就這麼簡單
    所以本篇就和小夥伴們分享基於內存操作的比較常用的分布式鎖——redis分布式鎖。手擼Redis分布式鎖實現原理redis分布式鎖實現原理其實也是比較簡單的,主要是依賴於redis的 set nx命令,我們來看一下完整的設置redis的命令:「Set resource_name my_random_value NX PX 30000」。
  • 還能用mysql實現分布式鎖?
    希望你我一同每日進步一點點。目錄前言單體應用鎖的局限性分布式鎖什麼是分布式鎖?設計思路實現方式基於資料庫的分布式鎖實戰寫在最後前言之前的文章中通過電商場景中秒殺的例子和大家分享了單體架構中鎖的使用方式,但是現在很多應用系統都是相當龐大的,很多應用系統都是微服務的架構體系,那麼在這種跨jvm的場景下,我們又該如何去解決並發。
  • 手寫Redis分布式鎖
    這時我們就需要一個全局的鎖了。實現全局的鎖不一定是Redis。MySQL,Zookeeper也可設計為分布式鎖。本篇主要講的是Redis分布式鎖的實現方式,其他的實現方式不做講解。MySQL用作分布式鎖在性能上並不好,這裡不建議使用。對Zookeeper分布式鎖有興趣的可以看看我寫的這篇文章。