天天看點

用redis實作分布式鎖時容易踩的5個坑

雲栖号資訊:【 點選檢視更多行業資訊

在這裡您可以找到不同行業的第一手的上雲資訊,還在等什麼,快來!

我的業務場景是這樣的,我們服務有庫存子產品,而我的服務又是多節點部署,要高峰期會存在庫存差異,後面分析問題之後,打算采用redis實作分布式鎖(主要的原因是服務已經內建了redis,不需要做額外的配置)。

踩坑1、資料庫事務逾時

不要感覺奇怪,分布式鎖怎麼會導緻資料庫事務逾時呢?

我的代碼大概是這樣的:

@Transaction(readOnly=false)
void update(){
    do{
        redis=JedisUtil.getJedis();
        flag = getLock(key,redis);
        if(flag){
            update();
        }
    }while(true)
}           

當你的key長時間擷取不到鎖,并且資料庫事務都有逾時時間的限制,那麼就會出現資料庫事務逾時問題;

解決方案:

資料庫事務改為手動送出事務。

踩坑2、redis key過期,而業務沒有執行完

我的key的過期時間設定的是30s,如果30秒業務還沒有執行完畢,鎖就會自動釋放,鎖釋放之後,其它線程又會去占用鎖,同樣會導緻問題的發生。

最簡單的解決方案就是使用redisson;

如果非要用redis來解決的話,隻能使用定時器去檢測key,如果說key還有2秒就快過期了,那麼再為key重新設定30秒的過期時間。

踩坑3、redis連接配接池爆滿

分布式鎖剛加上之後,生産出現一個問題,就是:

redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool

解決辦法:

開始查代碼,發現是開發人員沒有對連接配接進行釋放;

修複bug之後,又線上上跑了一段時間,又出現了

解決辦法

do{
        redis=JedisUtil.getJedis();
        flag = getLock(key,redis);
        if(flag){
            update();
        }else{
            // 釋放目前redis連接配接
            // 由于我們的業務場景屬于比較耗時的業務型,是以在這裡休眠1000毫秒
            redis.close();
            sleep(1000);
        }
    }while(true)
}           

目前請求擷取鎖,如果擷取不到,則釋放目前連接配接,并休眠一會;

合理配置redis連接配接池大小,主要參考具體業務場景的并發量來設定。

踩坑4、解鈴還須系鈴人

回顧一下加鎖的參數:

set(key, vlue,"NX","PX", 30000);複制代碼其中:value,我使用它來表示加鎖人,必須是一個唯一的辨別

比如:

A線程 key=test value=01

B線程 key=test value=02

如果A線程執行業務耗時超過了鎖的持有時間,鎖會自動釋放;鎖自動釋放之後,線程B又加鎖成功,但是,此時A線程執行完業務邏輯之後,去釋放鎖,但A線程的鎖已經自動釋放了,如果沒有value來辨別的話,它可能就會去釋放B線程的鎖。

踩坑5、redis叢集實作分布式鎖

這種情況我沒有遇到,因為公司的redis叢集做了改進;

先說一下這種問題産生的原因:

如果master節點由于某原因發生了主從切換,那麼就會出現鎖丢失的情況;

在master節點上拿到了鎖;

但是這個加鎖的key還沒有同步到slave節點;

master故障,發生故障轉移,slave節點更新為master節點;

故導緻鎖丢失;

需要通過使用redlock算法;

或使用redisson,它有對redlock算法做封裝。

【雲栖号線上課堂】每天都有産品技術專家分享!

課程位址:

https://yqh.aliyun.com/live

立即加入社群,與專家面對面,及時了解課程最新動态!

【雲栖号線上課堂 社群】

https://c.tb.cn/F3.Z8gvnK

原文釋出時間:2020-04-16

本文作者:Java圈子

本文來自:“

掘金

”,了解相關資訊可以關注“掘金”