最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Redis分布式鎖的超時問題及解決

 更新時間:2024年05月11日 09:43:22   作者:阿飛Sirx  
這篇文章主要介紹了Redis分布式鎖的超時問題及解決方案,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教

Redis分布式鎖的超時

Redis的分布式鎖并不能解決超時問題,如果在加鎖和釋放鎖之間的邏輯執(zhí)行得太長,以至于超出了鎖的超時限制,就會出現(xiàn)問題,因為這時候第一個線程持有的鎖過期了,臨界區(qū)的邏輯還沒有執(zhí)行完,而同時第二個線程就提前持有了這把鎖,導(dǎo)致臨界區(qū)代碼不能得到嚴(yán)格串行執(zhí)行

為了避免這個問題,分布式鎖不要用于較長時間的任務(wù),如果真的偶爾出現(xiàn)了問題,造成的數(shù)據(jù)小錯亂,可能需要人工介入解決

有一個稍微安全一點的方案,是將set指令的value參數(shù)設(shè)置為一個隨機數(shù),釋放鎖時先匹配隨機數(shù)是否一致,然后在刪除key,這是為了確保當(dāng)前線程占有的鎖不會被其他線程釋放,除非這個鎖是自動超時,但是匹配value,和刪除ke y不是一個原子操作,所以只是相對安全

分布式鎖失效問題

分布式鎖

1.1集群下的鎖失效問題

Synchronized中的重量級鎖,底層就是基于鎖監(jiān)視器(Monitor)來實現(xiàn)的。

簡單來說就是鎖對象頭會指向一個鎖監(jiān)視器,而在監(jiān)視器中則會記錄一些信息

比如:

  • _owner:持有鎖的線程
  • _recursions:鎖重入次數(shù)

因此每鎖一個對象。都會指向一個鎖監(jiān)視器,但是每個鎖監(jiān)視器同一時刻只能被一個線程持有,這樣再單機模式下,不同服務(wù)的JVM當(dāng)然不能通信,這樣就會出現(xiàn)鎖失效問題。所以在分布式環(huán)境下就不能使用Synchronized。所以分布式鎖一定要滿足多JVM都能訪問并且互斥的條件。

能滿足上述特征的組件有很多,因此實現(xiàn)分布式鎖的方式也非常多,例如:

  • 基于MySQL
  • 基于Redis
  • 基于Zookeeper
  • 基于ETCD

常見的最廣泛的應(yīng)用解決方式就是基于Redis實現(xiàn)的分布式鎖。

1.2.簡單分布式鎖

先來弄清原理,Redis的setnx命令是基于string操作的。

命令如下:

SETNX key value

當(dāng)且僅當(dāng)這個key不存在時setnx才能執(zhí)行成功,并且返回1,其它情況都會執(zhí)行失敗,并且返回0.我們就可以認(rèn)為返回值是1就是獲取鎖成功,返回值是0就是獲取鎖失敗,實現(xiàn)互斥效果。

當(dāng)業(yè)務(wù)執(zhí)行完成時,我們只需要通過DEL key命令刪除這個即可釋放鎖。這個時候其它線程又可以再次獲取鎖(執(zhí)行setnx成功)了。

不過我們要考慮一種極端的場景。獲取成功后,還沒釋放鎖時突然宕機,那么釋放鎖的動作就不會被執(zhí)行這就出現(xiàn)了死鎖。

# 獲取鎖,并記錄持有鎖的線程
SETNX lock thread1
# 設(shè)置過期時間,避免死鎖
EXPIRE lock 20我們可以利用Redis的KEY過期時間機制,在獲取鎖時給鎖添加一個超時時間:    

但是這顯然是兩條獨立的命令,如果我執(zhí)行完setnx后宕機,過期時間還未設(shè)置,死鎖問題又出現(xiàn)了!

為了保證兩條命令的原子性使用SET lock thread1 NX EX 20 就能保證原子性。對應(yīng)的api如下。

@RequiredArgsConstructor
public class RedisLock {
 
    private final String key;
    private final StringRedisTemplate redisTemplate;
 
    /**
     * 嘗試獲取鎖
     * @param leaseTime 鎖自動釋放時間
     * @param unit 時間單位
     * @return 是否獲取成功,true:獲取鎖成功;false:獲取鎖失敗
     */
    public boolean tryLock(long leaseTime, TimeUnit unit){
        // 1.獲取線程名稱
        String value = Thread.currentThread().getName();
        // 2.獲取鎖
        Boolean success = redisTemplate.opsForValue().setIfAbsent(key, value, leaseTime, unit);
        // 3.返回結(jié)果
        return BooleanUtils.isTrue(success);
    }
 
    /**
     * 釋放鎖
     */
    public void unlock(){
        redisTemplate.delete(key);
    }
}

1.3.分布式鎖的問題

1.3.1.鎖誤刪問題

第一個問題就是鎖誤刪問題,目前釋放鎖的操作是基于DEL,但是在極端情況下會出現(xiàn)問題。

假設(shè)場景,線程1獲取鎖成功完成執(zhí)行,準(zhǔn)備釋放鎖。

 但因為某些原因?qū)е箩尫沛i的操作被阻塞,直到超時放鎖

 

這時因為線程1被超時釋放,所以線程2拿到了鎖。這時候線程1醒了,給線程2的鎖刪了。

但此時線程2還是在執(zhí)行中,線程3在來的時候就會認(rèn)為現(xiàn)在沒人拿鎖,于是多個線程再次并發(fā)執(zhí)行,并發(fā)安全就可能再出現(xiàn)。

為了解決這種場景,我們可以在刪除鎖之前判斷當(dāng)前鎖的中保存的是否是當(dāng)前線程標(biāo)示,如果不是則證明不是自己的鎖,則不刪除;如果鎖標(biāo)示是當(dāng)前線程,則可以刪除。

1.3.2.超時釋放問題

  • 加上了鎖標(biāo)識判斷。
  • 可以避免大多數(shù)場景下的鎖誤刪問題,但是還是有極端情況。
  • 比如我線程1那所執(zhí)行完并且判斷完掛了,直到超時放鎖。
  • 這樣線程2來的時候是可以獲取鎖的,線程2去執(zhí)行業(yè)務(wù)中,線程1醒了,因為已經(jīng)通過了校驗,我給你鎖刪了,又發(fā)生了鎖誤刪問題。

總結(jié)起來,根源就在于判斷鎖標(biāo)識和刪除鎖是兩個動作,又不符合原子性了。

1.3.3分布式鎖的其他問題

  • 鎖的重入問題:同一個線程多次獲取鎖的場景,目前不支持,可能會導(dǎo)致死鎖
  • 鎖失敗的重試問題:獲取鎖失敗后要不要重試?目前是直接失敗,不支持重試
  • Redis主從的一致性問題:由于主從同步存在延遲,當(dāng)線程在主節(jié)點獲取鎖后,從節(jié)點可能未同步鎖信息。如果此時主宕機,會出現(xiàn)鎖失效情況。此時會有其它線程也獲取鎖成功。從而出現(xiàn)并發(fā)安全問題。

對應(yīng)的解決方案也有,就是比較麻煩

  • 原子性問題:可以利用Redis的LUA腳本來編寫鎖操作,確保原子性
  • 超時問題:利用WatchDog(看門狗)機制,獲取鎖成功時開啟一個定時任務(wù),在鎖到期前自動續(xù)期,避免超時釋放。而當(dāng)服務(wù)宕機后,WatchDog跟著停止運行,不會導(dǎo)致死鎖。
  • 鎖重入問題:可以模擬Synchronized原理,放棄setnx,而是利用Redis的Hash結(jié)構(gòu)來記錄鎖的持有者以及重入次數(shù),獲取鎖時重入次數(shù)+1,釋放鎖是重入次數(shù)-1,次數(shù)為0則鎖刪除
  • 主從一致性問題:可以利用Redis官網(wǎng)推薦的RedLock機制來解決

我們自己手寫解決不僅繁瑣,而且實現(xiàn)起來耗費時間,所以我們可以使用開源的框架來實現(xiàn)分布式鎖。其中比較完善的一個第三方組件就是Redisson

總結(jié)

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Redis中的連接命令與鍵命令操作詳解

    Redis中的連接命令與鍵命令操作詳解

    Redis連接命令主要是用于客戶端與服務(wù)器建立連接的,Redis是一種流行的內(nèi)存數(shù)據(jù)庫,支持多種數(shù)據(jù)結(jié)構(gòu),其中鍵命令是核心操作之一,在Redis中,鍵(Key)是用來存儲數(shù)據(jù)的主要元素,每個鍵都有一個唯一的名稱,本文給大家介紹了Redis中的連接命令與鍵命令操作
    2024-09-09
  • Redis中緩存預(yù)熱與緩存穿透解決方案

    Redis中緩存預(yù)熱與緩存穿透解決方案

    Redis緩存預(yù)熱與緩存穿透是Redis緩存使用中的兩個重要概念,文章首先介紹了Redis緩存預(yù)熱和緩存穿透的基本概念,然后詳細(xì)闡述了它們的產(chǎn)生原因和解決方案,感興趣的可以了解一下
    2023-12-12
  • redis如何查看鎖是否存在

    redis如何查看鎖是否存在

    文章介紹了兩種方法來檢查Redis鎖的狀態(tài):使用GET命令查看鎖的值和使用EXISTS命令檢查鎖的存在性,這兩種方法都是通過連接到Redis服務(wù)器并執(zhí)行相應(yīng)的命令來實現(xiàn)的,GET命令用于獲取指定鍵的值,而EXISTS命令用于檢查指定鍵是否存在
    2025-01-01
  • Redis避免以后AOF再次損壞的預(yù)防措施

    Redis避免以后AOF再次損壞的預(yù)防措施

    文章提供了修復(fù)和防止Redis AOF文件損壞的方法,包括臨時修復(fù)、關(guān)鍵配置設(shè)置、雙保險策略、運維規(guī)范和一鍵自救命令,重點介紹了安全AOF刷盤、AOF自動碎片重寫、損壞容錯配置、關(guān)閉混合AOF等配置,并強調(diào)了避免磁盤滿、定期巡檢等運維規(guī)范,需要的朋友可以參考下
    2026-04-04
  • 淺談RedisTemplate和StringRedisTemplate的區(qū)別

    淺談RedisTemplate和StringRedisTemplate的區(qū)別

    本文主要介紹了RedisTemplate和StringRedisTemplate的區(qū)別及個人見解,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-06-06
  • Redis腦裂導(dǎo)致數(shù)據(jù)丟失的解決

    Redis腦裂導(dǎo)致數(shù)據(jù)丟失的解決

    本文主要介紹了Redis腦裂導(dǎo)致數(shù)據(jù)丟失的解決,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-01-01
  • Redis的Expire與Setex區(qū)別說明

    Redis的Expire與Setex區(qū)別說明

    這篇文章主要介紹了Redis的Expire與Setex區(qū)別說明,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-09-09
  • 一文搞懂Redis中的慢查詢?nèi)罩竞捅O(jiān)視器

    一文搞懂Redis中的慢查詢?nèi)罩竞捅O(jiān)視器

    我們都知道MySQL有慢查詢?nèi)罩?但Redis也有慢查詢?nèi)罩?可用于監(jiān)視和優(yōu)化查詢,本文給大家詳細(xì)介紹了Redis中的慢查詢?nèi)罩竞捅O(jiān)視器,文章通過代碼示例講解的非常詳細(xì),需要的朋友可以參考下
    2024-04-04
  • Redis集群部署模式的不同實現(xiàn)過程

    Redis集群部署模式的不同實現(xiàn)過程

    這篇文章主要介紹了Redis集群部署模式的不同實現(xiàn)過程,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2025-05-05
  • Window下對Redis進行開啟與關(guān)閉的操作方法

    Window下對Redis進行開啟與關(guān)閉的操作方法

    這篇文章主要介紹了Window下對Redis進行開啟與關(guān)閉的操作方法,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-11-11

最新評論

阿鲁科尔沁旗| 天全县| 内江市| 井冈山市| 遵义市| 南郑县| 崇信县| 湖口县| 太谷县| 红河县| 伊春市| 徐闻县| 高阳县| 社旗县| 象山县| 深泽县| 静安区| 永登县| 吉木萨尔县| 东兰县| 锦州市| 三都| 信宜市| 文安县| 肥乡县| 灌云县| 砀山县| 万安县| 商水县| 措美县| 霍林郭勒市| 电白县| 封丘县| 孝感市| 文登市| 镶黄旗| 南木林县| 那坡县| 右玉县| 荣成市| 蒲城县|