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

解決Redis分布式鎖的誤刪問(wèn)題和原子性問(wèn)題

 更新時(shí)間:2024年02月11日 08:53:37   作者:在下小吉.  
Redis的分布式鎖是通過(guò)利用Redis的原子操作和特性來(lái)實(shí)現(xiàn)的,為了保證數(shù)據(jù)的一致性和避免沖突,可以使用分布式鎖來(lái)進(jìn)行同步控制,本文給大家介紹了如何解決Redis分布式鎖的誤刪問(wèn)題和原子性問(wèn)題,需要的朋友可以參考下

Redis的分布式鎖

Redis的分布式鎖是通過(guò)利用Redis的原子操作和特性來(lái)實(shí)現(xiàn)的。在分布式環(huán)境中,多個(gè)應(yīng)用程序或服務(wù)可能同時(shí)訪問(wèn)共享資源,為了保證數(shù)據(jù)的一致性和避免沖突,可以使用分布式鎖來(lái)進(jìn)行同步控制。

以下是一種常見(jiàn)的使用Redis實(shí)現(xiàn)分布式鎖的方式:

  • 獲取鎖:當(dāng)一個(gè)應(yīng)用程序需要獲取鎖時(shí),它可以通過(guò)執(zhí)行以下操作在Redis中設(shè)置一個(gè)特定的鍵值對(duì):
SET lock_key unique_value NX PX lock_timeout

這里的lock_key是鎖的唯一標(biāo)識(shí),unique_value是唯一的值,可以是隨機(jī)生成的UUID,NX表示只有當(dāng)鍵不存在時(shí)才會(huì)設(shè)置成功,PX表示設(shè)置鍵的過(guò)期時(shí)間。通過(guò)設(shè)置過(guò)期時(shí)間,即使獲取鎖的應(yīng)用程序崩潰或異常退出,鎖也會(huì)在一段時(shí)間后自動(dòng)釋放,避免出現(xiàn)死鎖。

  • 釋放鎖:當(dāng)應(yīng)用程序完成對(duì)共享資源的操作后,它可以通過(guò)執(zhí)行以下操作釋放鎖:
if GET lock_key == unique_value then
    DELETE lock_key
end

應(yīng)用程序首先獲取鎖的當(dāng)前值,然后比較是否與自己持有的唯一值相等,如果相等則刪除該鍵,表示釋放鎖。這樣可以確保只有持有鎖的應(yīng)用程序才能釋放鎖,避免誤釋放其他應(yīng)用程序的鎖。

需要注意的是,分布式鎖并不是絕對(duì)安全和可靠的。在高并發(fā)的環(huán)境中,可能存在競(jìng)爭(zhēng)條件和死鎖等問(wèn)題。因此,在實(shí)際使用中,需要考慮更復(fù)雜的場(chǎng)景和解決方案。

誤刪問(wèn)題

遇到下面的情況的話,會(huì)出現(xiàn)Redis分布式鎖的誤刪問(wèn)題

這種情況下。線程1首先獲取鎖,但是發(fā)生了阻塞,于是線程2拿到了執(zhí)行權(quán),在線程2執(zhí)行的過(guò)程中,線程1蘇醒了,繼續(xù)執(zhí)行,到后面,線程1執(zhí)行到了刪除鎖的操作,此時(shí)就會(huì)把本應(yīng)該屬于線程2的鎖刪除,這樣子就造成了誤刪問(wèn)題

解決方法

就是在每個(gè)線程釋放鎖的時(shí)候,去判斷一下當(dāng)前這把鎖是否屬于自己,如果屬于自己,則不進(jìn)行鎖的刪除,假設(shè)還是上邊的情況,線程1卡頓,鎖自動(dòng)釋放,線程2進(jìn)入到鎖的內(nèi)部執(zhí)行邏輯,此時(shí)線程1反應(yīng)過(guò)來(lái),然后刪除鎖,但是線程1,一看當(dāng)前這把鎖不是屬于自己,于是不進(jìn)行刪除鎖邏輯,當(dāng)線程2走到刪除鎖邏輯時(shí),如果沒(méi)有卡過(guò)自動(dòng)釋放鎖的時(shí)間點(diǎn),則判斷當(dāng)前這把鎖是屬于自己的,于是刪除這把鎖。

代碼實(shí)現(xiàn)

public class SimpleRedisLock implements ILock {

    private String name;
    private StringRedisTemplate stringRedisTemplate;

    public SimpleRedisLock(String name, StringRedisTemplate stringRedisTemplate) {
        this.name = name;
        this.stringRedisTemplate = stringRedisTemplate;
    }

    private static final String KEY_PREFIX = "lock:";
    //使用uuid,在獲取鎖的時(shí)候存入線程標(biāo)識(shí)
    private static final String ID_PREFIX = UUID.randomUUID().toString(true) + "-";


    @Override
    public boolean tryLock(long timeoutSec) {
        // 獲取線程標(biāo)示
        String threadId = ID_PREFIX + Thread.currentThread().getId();
        // 獲取鎖
        Boolean success = stringRedisTemplate.opsForValue()
                .setIfAbsent(KEY_PREFIX + name, threadId, timeoutSec, TimeUnit.SECONDS);
        return Boolean.TRUE.equals(success);

        //這里不能是return success;否則  因?yàn)閜ublic后面的boolean是基本類型,而Boolean是引用類型,如果直接返回success,是一個(gè)自動(dòng)拆箱的過(guò)程,可能回發(fā)生空指針異常
    }

   	@Override
    public void unlock() {
        // 獲取線程標(biāo)示
        String threadId = ID_PREFIX + Thread.currentThread().getId();
        // 獲取鎖中的標(biāo)示
        String id = stringRedisTemplate.opsForValue().get(KEY_PREFIX + name);
        // 判斷標(biāo)示是否一致
        if(threadId.equals(id)) {
            // 釋放鎖
            stringRedisTemplate.delete(KEY_PREFIX + name);
        }
    }
}

原子性問(wèn)題

上面我們解決了誤刪問(wèn)題
在誤刪問(wèn)題的情況下,遇到下面的情況的話,會(huì)出現(xiàn)Redis分布式鎖的原子性問(wèn)題

這種情況下,線程1先執(zhí)行一段,線程1先判斷鎖標(biāo)識(shí),判斷成功,標(biāo)識(shí)是屬于線程1的,后面就在線程1正準(zhǔn)備刪除鎖釋放的過(guò)程中,突然線程1的鎖過(guò)期了,線程1發(fā)生阻塞
這個(gè)時(shí)候線程2開(kāi)始執(zhí)行,在線程2執(zhí)行過(guò)程中,線程1阻塞結(jié)束了,會(huì)執(zhí)行刪除鎖的操作,相當(dāng)于判斷鎖標(biāo)識(shí)并沒(méi)有起到作用(因?yàn)橹耙痪渑袛噙^(guò)了),于是就把線程2的鎖給刪除掉了,又一次發(fā)生了誤刪操作
這個(gè)時(shí)候線程3趁虛而入,執(zhí)行業(yè)務(wù)
這就是刪鎖時(shí)的原子性問(wèn)題,之所以有這個(gè)問(wèn)題,是因?yàn)榕袛噫i標(biāo)識(shí)和刪除鎖是2個(gè)動(dòng)作,這2個(gè)動(dòng)作中間產(chǎn)生了阻塞
那么我們就要讓這2個(gè)操作一起執(zhí)行,中間不能出現(xiàn)間隔

Lua腳本

Redis提供了Lua腳本功能,在一個(gè)腳本中編寫多條Redis命令,確保多條命令執(zhí)行時(shí)的原子性。Lua是一種編程語(yǔ)言,它的基本語(yǔ)法大家可以參考網(wǎng)站:https://www.runoob.com/lua/lua-tutorial.html,這里重點(diǎn)介紹Redis提供的調(diào)用函數(shù),我們可以使用lua去操作redis,又能保證他的原子性,這樣就可以實(shí)現(xiàn)拿鎖比鎖刪鎖是一個(gè)原子性動(dòng)作了,作為Java程序員這一塊并不作一個(gè)簡(jiǎn)單要求,并不需要大家過(guò)于精通,只需要知道他有什么作用即可。

這里重點(diǎn)介紹Redis提供的調(diào)用函數(shù),語(yǔ)法如下:

redis.call('命令名稱', 'key', '其它參數(shù)', ...)

例如,我們要執(zhí)行set name jack,則腳本是這樣:

# 執(zhí)行 set name jack
redis.call('set', 'name', 'jack')

例如,我們要先執(zhí)行set name Rose,再執(zhí)行g(shù)et name,則腳本如下:

# 先執(zhí)行 set name jack
redis.call('set', 'name', 'Rose')
# 再執(zhí)行 get name
local name = redis.call('get', 'name')
# 返回
return name

寫好腳本以后,需要用Redis命令來(lái)調(diào)用腳本,調(diào)用腳本的常見(jiàn)命令如下:

例如,我們要執(zhí)行 redis.call(‘set’, ‘name’, ‘jack’) 這個(gè)腳本,語(yǔ)法如下:

如果腳本中的key、value不想寫死,可以作為參數(shù)傳遞。key類型參數(shù)會(huì)放入KEYS數(shù)組,其它參數(shù)會(huì)放入ARGV數(shù)組,在腳本中可以從KEYS和ARGV數(shù)組獲取這些參數(shù):

利用Java代碼調(diào)用Lua腳本改造分布式鎖

接下來(lái)我們來(lái)回一下我們釋放鎖的邏輯:

釋放鎖的業(yè)務(wù)流程是這樣的

1、獲取鎖中的線程標(biāo)示

? 2、判斷是否與指定的標(biāo)示(當(dāng)前線程標(biāo)示)一致

? 3、如果一致則釋放鎖(刪除)

? 4、如果不一致則什么都不做

如果用Lua腳本來(lái)表示則是這樣的:

最終我們操作redis的拿鎖比鎖刪鎖的lua腳本就會(huì)變成這樣

-- 這里的 KEYS[1] 就是鎖的key,這里的ARGV[1] 就是當(dāng)前線程標(biāo)示
-- 獲取鎖中的標(biāo)示,判斷是否與當(dāng)前線程標(biāo)示一致
if (redis.call('GET', KEYS[1]) == ARGV[1]) then
  -- 一致,則刪除鎖
  return redis.call('DEL', KEYS[1])
end
-- 不一致,則直接返回
return 0

lua腳本本身并不需要大家花費(fèi)太多時(shí)間去研究,只需要知道如何調(diào)用,大致是什么意思即可,所以在筆記中并不會(huì)詳細(xì)的去解釋這些lua表達(dá)式的含義。

我們的RedisTemplate中,可以利用execute方法去執(zhí)行l(wèi)ua腳本,參數(shù)對(duì)應(yīng)關(guān)系就如下圖

代碼實(shí)現(xiàn)

我們先寫入lua這個(gè)腳本

-- 比較線程標(biāo)示與鎖中的標(biāo)示是否一致
if(redis.call('get', KEYS[1]) ==  ARGV[1]) then
    -- 釋放鎖 del key
    return redis.call('del', KEYS[1])
end
return 0

然后我們來(lái)調(diào)用這個(gè)腳本

下面是完整代碼

public class SimpleRedisLock implements ILock {

    private String name;
    private StringRedisTemplate stringRedisTemplate;

    public SimpleRedisLock(String name, StringRedisTemplate stringRedisTemplate) {
        this.name = name;
        this.stringRedisTemplate = stringRedisTemplate;
    }

    private static final String KEY_PREFIX = "lock:";
    private static final String ID_PREFIX = UUID.randomUUID().toString(true) + "-";
    private static final DefaultRedisScript<Long> UNLOCK_SCRIPT;
    static {
        UNLOCK_SCRIPT = new DefaultRedisScript<>();
        UNLOCK_SCRIPT.setLocation(new ClassPathResource("unlock.lua"));
        UNLOCK_SCRIPT.setResultType(Long.class);
    }

    @Override
    public boolean tryLock(long timeoutSec) {
        // 獲取線程標(biāo)示
        String threadId = ID_PREFIX + Thread.currentThread().getId();
        // 獲取鎖
        Boolean success = stringRedisTemplate.opsForValue()
                .setIfAbsent(KEY_PREFIX + name, threadId, timeoutSec, TimeUnit.SECONDS);
        return Boolean.TRUE.equals(success);

        //這里不能是return success;否則  因?yàn)閜ublic后面的boolean是基本類型,而Boolean是引用類型,如果直接返回success,是一個(gè)自動(dòng)拆箱的過(guò)程,可能回發(fā)生空指針異常
    }

    @Override
    public void unlock() {
        // 調(diào)用lua腳本
        stringRedisTemplate.execute(
                UNLOCK_SCRIPT,
                Collections.singletonList(KEY_PREFIX + name),
                ID_PREFIX + Thread.currentThread().getId());
    }
}


在技術(shù)的道路上,我們不斷探索、不斷前行,不斷面對(duì)挑戰(zhàn)、不斷突破自我??萍嫉陌l(fā)展改變著世界,而我們作為技術(shù)人員,也在這個(gè)過(guò)程中書寫著自己的篇章。讓我們攜手并進(jìn),共同努力,開(kāi)創(chuàng)美好的未來(lái)!愿我們?cè)诳萍嫉恼魍旧喜粩鄪^進(jìn),創(chuàng)造出更加美好、更加智能的明天!

以上就是解決Redis分布式鎖的誤刪問(wèn)題和原子性問(wèn)題的詳細(xì)內(nèi)容,更多關(guān)于Redis分布式鎖誤刪和原子性問(wèn)題的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 搭建Caffeine+Redis多級(jí)緩存機(jī)制

    搭建Caffeine+Redis多級(jí)緩存機(jī)制

    本文主要介紹了搭建Caffeine+Redis多級(jí)緩存機(jī)制,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2025-07-07
  • Redis客戶端連接機(jī)制的實(shí)現(xiàn)方案

    Redis客戶端連接機(jī)制的實(shí)現(xiàn)方案

    本文主要介紹了Redis客戶端連接機(jī)制的實(shí)現(xiàn)方案,包括事件驅(qū)動(dòng)模型、非阻塞I/O處理、連接池應(yīng)用及配置優(yōu)化,具有一定的參考價(jià)值,感興趣的可以了解一下
    2025-07-07
  • Redis統(tǒng)計(jì)獨(dú)立用戶訪問(wèn)量的四種方案

    Redis統(tǒng)計(jì)獨(dú)立用戶訪問(wèn)量的四種方案

    Redis 提供了多種數(shù)據(jù)結(jié)構(gòu)來(lái)高效實(shí)現(xiàn) UV 統(tǒng)計(jì),各有優(yōu)劣,本文將詳細(xì)對(duì)比 Set、Bitmap、HyperLogLog、incr + 日期維度(即用戶提到的兩種方式)四種方案,并通過(guò)流程圖和代碼示例幫助你選型,需要的朋友可以參考下
    2026-04-04
  • Redis常用命令集的使用

    Redis常用命令集的使用

    作為一名Redis開(kāi)發(fā)者或管理員,熟練掌握Redis的常用命令是必不可少的,本文主要介紹了Redis常用命令集的使用,具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-11-11
  • Jackson2JsonRedisSerializer和GenericJackson2JsonRedisSerializer區(qū)別

    Jackson2JsonRedisSerializer和GenericJackson2JsonRedisSerializ

    本文主要介紹了Jackson2JsonRedisSerializer和GenericJackson2JsonRedisSerializer區(qū)別,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-04-04
  • 無(wú)法啟動(dòng)Redis打開(kāi)redis-server閃退的問(wèn)題解決辦法

    無(wú)法啟動(dòng)Redis打開(kāi)redis-server閃退的問(wèn)題解決辦法

    正常開(kāi)啟redis服務(wù),首先要啟動(dòng)redis-server.exe,但是閃退,導(dǎo)致無(wú)法開(kāi)啟redis服務(wù),這篇文章主要給大家介紹了關(guān)于無(wú)法啟動(dòng)Redis打開(kāi)redis-server閃退問(wèn)題的解決辦法,需要的朋友可以參考下
    2024-07-07
  • 高效異步redis客戶端aredis優(yōu)劣勢(shì)原理解析

    高效異步redis客戶端aredis優(yōu)劣勢(shì)原理解析

    這篇文章主要介紹了高效異步redis客戶端aredis優(yōu)劣勢(shì)原理解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-09-09
  • 關(guān)于Redis數(shù)據(jù)庫(kù)三種持久化方案介紹

    關(guān)于Redis數(shù)據(jù)庫(kù)三種持久化方案介紹

    大家好,本篇文章主要講的是關(guān)于Redis數(shù)據(jù)庫(kù)三種持久化方案介紹,感興趣的同學(xué)趕快來(lái)看一看吧,對(duì)你有幫助的話記得收藏一下
    2022-01-01
  • Redis的Spring客戶端使用小結(jié)

    Redis的Spring客戶端使用小結(jié)

    在Spring中使用Redis,可以極大地提升應(yīng)用程序的性能和響應(yīng)速度,本文主要介紹了Redis的Spring客戶端使用小結(jié),具有一定的參考價(jià)值,感興趣的可以了解一下
    2025-04-04
  • Redis過(guò)期時(shí)間的設(shè)計(jì)與實(shí)現(xiàn)代碼

    Redis過(guò)期時(shí)間的設(shè)計(jì)與實(shí)現(xiàn)代碼

    在?Redis?中,鍵的過(guò)期時(shí)間設(shè)計(jì)與實(shí)現(xiàn)是一個(gè)重要的功能,這使得?Redis?可以自動(dòng)刪除在指定時(shí)間后不再需要的鍵,下面詳細(xì)介紹?Redis?過(guò)期時(shí)間的設(shè)計(jì)和實(shí)現(xiàn),包括設(shè)置過(guò)期時(shí)間、過(guò)期鍵的存儲(chǔ)結(jié)構(gòu)、過(guò)期鍵的刪除策略等,需要的朋友可以參考下
    2024-08-08

最新評(píng)論

陆良县| 江油市| 崇州市| 福海县| 政和县| 临洮县| 华坪县| 蕉岭县| 朝阳市| 韩城市| 弋阳县| 营口市| 福泉市| 泗洪县| 南雄市| 陈巴尔虎旗| 桃园县| 华蓥市| 股票| 海晏县| 新和县| 宣威市| 肇州县| 新沂市| 成都市| 福鼎市| 浙江省| 怀柔区| 临江市| 惠东县| 雷山县| 兴安盟| 台湾省| 冀州市| 武陟县| 金溪县| 巴林左旗| 鹿邑县| 清原| 松桃| 景洪市|