Redis分布式鎖原理及性能優(yōu)化

一、什么是分布式鎖

在單體架構(gòu)中,系統(tǒng)只有一個(gè),系統(tǒng)所用的內(nèi)存和進(jìn)程也只有一個(gè),多個(gè)線程可以共享同一份數(shù)據(jù)。這樣只要使用java提供的鎖機(jī)制就可以解決并發(fā)訪問帶來的問題,但是分布式系統(tǒng)中,系統(tǒng)是多個(gè),并且所使用的內(nèi)存也是不同的,每個(gè)系統(tǒng)也都有獨(dú)立的進(jìn)程,這樣Java提供的鎖就沒辦法解決分布式系統(tǒng)中的并發(fā)訪問問題。因此就需要引入分布式鎖來解決分布式系統(tǒng)中共享資源訪問的問題。

1.2 分布式鎖的特性

1.要保證同一時(shí)刻內(nèi),只有一個(gè)服務(wù)獲取到這個(gè)鎖
2.這把鎖要能重入,在某些業(yè)務(wù)下會(huì)出現(xiàn)同一把鎖的重入
3.具備鎖續(xù)命,保證高并發(fā)下不會(huì)因鎖的時(shí)效問題引起數(shù)據(jù)錯(cuò)誤
4.具有非阻塞式獲取鎖,在獲取鎖失敗后立刻返回

1.3 基于Redisson實(shí)現(xiàn)的分布式鎖

Redisson中分布式鎖的架構(gòu):
image

關(guān)于Redisson分布式鎖的使用:

private Integer setInfo(Long key){
    // 獲取分布式鎖
    RLock lock = redisson.getLock("Ext_Info:" + key);
    // 加鎖
    lock.lock();
    try{
        // 業(yè)務(wù)處理
        Product product = productDao.get(key);
        return 1;
    }catch (Exception e){
        e.printStackTrace();
    }finally {
        // 解鎖
        lock.unlock();
    }
    return  -1;
}

1.4 分布式鎖失效問題

當(dāng)主從或集群機(jī)構(gòu)的主節(jié)點(diǎn)掛掉后,該節(jié)點(diǎn)存儲(chǔ)的分布式鎖信息可能會(huì)丟失。而當(dāng)從節(jié)點(diǎn)選舉成新節(jié)點(diǎn)后,其它線程又可以對(duì)新的主節(jié)點(diǎn)進(jìn)行添加分布式鎖信息,這就會(huì)引起并發(fā)安全問題。

為了解決這個(gè)問題,就引入了redlock。
redlock的邏輯就是向多個(gè)節(jié)點(diǎn)寫入加鎖的信息,如果寫入成功,加代表加鎖成功。但是如果寫入的節(jié)點(diǎn)丟失了鎖的信息,仍然是會(huì)出現(xiàn)分布式鎖失效問題。
還有一點(diǎn)就是性能問題,我們使用redis就是為了它的高性能,但是使用redlock后每次加鎖都要寫入多個(gè)節(jié)點(diǎn),這就會(huì)降低redis性能,這樣還不如使用zookeeper。


image.png

二、Redisson實(shí)現(xiàn)分布式鎖源碼剖析

加鎖的核心邏輯:
image

加鎖成功后的鎖續(xù)命邏輯:
image

其它線程加鎖失敗后的邏輯:
image

解鎖核心邏輯:
image

三、分布式鎖的優(yōu)化

對(duì)于讀多寫少的業(yè)務(wù),可以使用readwirte鎖:

public Product update(Product product) {
    Product productResult = null;
    // 獲取分布式鎖
    RReadWriteLock readWriteLock = redisson.getReadWriteLock(LOCK_PRODUCT_UPDATE_PREFIX + product.getId());
    // 寫鎖
    RLock writeLock = readWriteLock.writeLock();
    // 加鎖
    writeLock.lock();
    try {
        productResult = productDao.update(product);
        // 設(shè)置過期時(shí)間
        redisUtil.set(productResult.getId(), JSON.toJSONString(productResult),
                genProductCacheTimeout(), TimeUnit.SECONDS);
    } finally {
        // 釋放鎖
        writeLock.unlock();
    }
    return productResult;
}

redisson實(shí)現(xiàn)的讀寫鎖,大體上和上面的分布式鎖邏輯相同,只是加了一個(gè)mode,用于區(qū)分是讀還是寫。對(duì)于讀讀而言,就等于是鎖的重入,不會(huì)阻塞;對(duì)于讀寫、寫寫操作,就會(huì)阻塞保證并發(fā)的安全。

四、緩存問題

1、什么是緩存擊穿?

當(dāng)同一時(shí)刻有大量的緩存失效,就會(huì)導(dǎo)致大量的請(qǐng)求打到數(shù)據(jù)庫,會(huì)造成數(shù)據(jù)庫壓力過大甚至宕機(jī)。
解決辦法:在給緩存數(shù)據(jù)設(shè)置過期時(shí)間時(shí),增加一個(gè)隨機(jī)的擾動(dòng)因子,避免讓大量的緩存數(shù)據(jù)都同一時(shí)刻失效。

2、什么是緩存穿透?

緩存層和數(shù)據(jù)庫都沒有數(shù)據(jù),每次請(qǐng)求都會(huì)落到數(shù)據(jù)庫,如果是高并發(fā)場(chǎng)景下,就會(huì)引起數(shù)據(jù)的壓力劇增,甚至宕機(jī)。
解決辦法:
1、對(duì)于不存在的數(shù)據(jù),可以在緩存層面設(shè)置對(duì)應(yīng)key的空值
2、布隆過濾器,向布隆過濾器中添加key時(shí),會(huì)先使用多個(gè)hash函數(shù)進(jìn)行運(yùn)算,然后定位到布隆過濾器的數(shù)組中某個(gè)下標(biāo)。當(dāng)某個(gè)key查詢布隆過濾器數(shù)據(jù)時(shí),會(huì)同樣使用多個(gè)hash函數(shù)進(jìn)行運(yùn)算,然后得到數(shù)組中的下標(biāo)位置。

3、什么是緩存雪崩?

緩存層的作用就是分擔(dān)數(shù)據(jù)庫層面的壓力,如果緩存層宕機(jī),大量的請(qǐng)求就直接打到數(shù)據(jù)庫,高并發(fā)下數(shù)據(jù)庫有可能宕機(jī)。
解決辦法:
1、確保緩存層的高可用,比如搭建集群、主從架構(gòu)
2、在后端應(yīng)用上設(shè)置限流或服務(wù)降級(jí)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容