日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
深入了解redis分布式鎖

深入理解redis分布式鎖

哈嘍,大家好,我是指北君。

成都創(chuàng)新互聯(lián)公司長期為超過千家客戶提供的網(wǎng)站建設(shè)服務(wù),團(tuán)隊(duì)從業(yè)經(jīng)驗(yàn)10年,關(guān)注不同地域、不同群體,并針對不同對象提供差異化的產(chǎn)品和服務(wù);打造開放共贏平臺,與合作伙伴共同營造健康的互聯(lián)網(wǎng)生態(tài)環(huán)境。為濰城企業(yè)提供專業(yè)的網(wǎng)站建設(shè)、做網(wǎng)站,濰城網(wǎng)站改版等技術(shù)服務(wù)。擁有十載豐富建站經(jīng)驗(yàn)和眾多成功案例,為您定制開發(fā)。

本篇文件我們來介紹如何Redis實(shí)現(xiàn)分布式鎖的演進(jìn)過程,以及為什么不能直接用Setnx實(shí)現(xiàn)分布式鎖。

1、分布式鎖簡介

分布式鎖是控制分布式系統(tǒng)不同進(jìn)程共同訪問共享資源的一種鎖的實(shí)現(xiàn)。如果不同的系統(tǒng)或同一個系統(tǒng)的不同主機(jī)之間共享了某個臨界資源,往往需要互斥來防止彼此干擾,以保證一致性。

業(yè)界流行的分布式鎖實(shí)現(xiàn),一般有這3種方式:

  • 基于數(shù)據(jù)庫實(shí)現(xiàn)的分布式鎖
  • 基于Redis實(shí)現(xiàn)的分布式鎖
  • 基于Zookeeper實(shí)現(xiàn)的分布式鎖

這里主要介紹如何通過 Redis 來實(shí)現(xiàn)分布式鎖。在介紹 Redis 分布式鎖之前,我們首先介紹一下實(shí)現(xiàn)Redis 分布式鎖的關(guān)鍵命令。

2、setnx

setnx key value

Setnx(SET if Not eXists) 命令在指定的 key 不存在時,為 key 設(shè)置指定的值。

設(shè)置成功,返回 1 。設(shè)置失敗,返回 0 。

PS:Redis 官方是不推薦基于 setnx 命令來實(shí)現(xiàn)分布式鎖的,因?yàn)闀嬖诤芏鄦栴},

①、單點(diǎn)問題。比如:

  • 客戶端A 從master拿到鎖lock01
  • master正要把lock01同步(Redis的主從同步通常是異步的)給slave時,突然宕機(jī)了,導(dǎo)致lock01沒同步給slave
  • 主從切換,slave節(jié)點(diǎn)被晉級為master節(jié)點(diǎn)
  • 客戶端B到master拿lock01照樣能拿到。這樣必將導(dǎo)致同一把鎖被多人使用。

②、鎖的高級用法,比如讀寫鎖、可重入鎖等等,setnx 都比較難實(shí)現(xiàn)。

這里先介紹基于 sentnx 實(shí)現(xiàn)的分布式鎖,后面會介紹官方推薦的基于 redisson 來實(shí)現(xiàn)分布式鎖。

3、Redis-分布式鎖-階段1

接到上文,查詢?nèi)壏诸悢?shù)據(jù),如果我們部署了多個商品服務(wù),然后多個線程同時去獲取三級分類數(shù)據(jù),如果不加分布式鎖,就會導(dǎo)致,每一個部署的商品服務(wù)第一次查詢都會走 DB。

public Map> getCatelogJsonWithRedisLock() throws InterruptedException {
// 一、獲取分布式鎖
Boolean lock = stringRedisTemplate.opsForValue().setIfAbsent("lock", "111");

if(lock){
// true 表示加鎖成功,執(zhí)行相關(guān)業(yè)務(wù)
Map> dataFromDb = getDataFromDb();
stringRedisTemplate.delete("lock");
return dataFromDb;
}else{
System.out.println("獲取分布式鎖失敗...等待重試...");
//加鎖失敗...重試機(jī)制
//休眠一百毫秒
try {
TimeUnit.MILLISECONDS.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
//自旋的方式
return getCatelogJsonWithRedisLock();
}
}

4、Redis-分布式鎖-階段2

設(shè)置鎖自動過期

public Map> getCatelogJsonWithRedisLock() throws InterruptedException {
// 一、獲取分布式鎖
Boolean lock = stringRedisTemplate.opsForValue().setIfAbsent("lock", "111");

if(lock){
// true 表示加鎖成功,執(zhí)行相關(guān)業(yè)務(wù)
// 設(shè)置過期時間
stringRedisTemplate.expire("lock",30,TimeUnit.SECONDS);
Map> dataFromDb = getDataFromDb();
stringRedisTemplate.delete("lock");
return dataFromDb;
}else{
System.out.println("獲取分布式鎖失敗...等待重試...");
//加鎖失敗...重試機(jī)制
//休眠一百毫秒
try {
TimeUnit.MILLISECONDS.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
//自旋的方式
return getCatelogJsonWithRedisLock();
}
}

5、Redis-分布式鎖-階段3

setnx 命令和過期時間保證原子性。

public Map> getCatelogJsonWithRedisLock() throws InterruptedException {
// 一、獲取分布式鎖
Boolean lock = stringRedisTemplate.opsForValue().setIfAbsent("lock", "111",30,TimeUnit.SECONDS);

if(lock){
// true 表示加鎖成功,執(zhí)行相關(guān)業(yè)務(wù)
// 設(shè)置過期時間
//stringRedisTemplate.expire("lock",30,TimeUnit.SECONDS);
Map> dataFromDb = getDataFromDb();
stringRedisTemplate.delete("lock");
return dataFromDb;
}else{
System.out.println("獲取分布式鎖失敗...等待重試...");
//加鎖失敗...重試機(jī)制
//休眠一百毫秒
try {
TimeUnit.MILLISECONDS.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
//自旋的方式
return getCatelogJsonWithRedisLock();
}
}

6、Redis-分布式鎖-階段4

保證刪除的是自己的鎖。

public Map> getCatelogJsonWithRedisLock() throws InterruptedException {
// 一、獲取分布式鎖
String uuid = UUID.randomUUID().toString();
Boolean lock = stringRedisTemplate.opsForValue().setIfAbsent("lock", uuid,30,TimeUnit.SECONDS);

if(lock){
// true 表示加鎖成功,執(zhí)行相關(guān)業(yè)務(wù)
// 設(shè)置過期時間
//stringRedisTemplate.expire("lock",30,TimeUnit.SECONDS);
Map> dataFromDb = getDataFromDb();
String lockValue = stringRedisTemplate.opsForValue().get("lock");
if(uuid.equals(lockValue)){
stringRedisTemplate.delete("lock");
}
return dataFromDb;
}else{
System.out.println("獲取分布式鎖失敗...等待重試...");
//加鎖失敗...重試機(jī)制
//休眠一百毫秒
try {
TimeUnit.MILLISECONDS.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
//自旋的方式
return getCatelogJsonWithRedisLock();
}
}

7、Redis-分布式鎖-階段5

通過Lua腳本保證刪除鎖和判斷鎖兩個操作原子性

public Map> getCatelogJsonWithRedisLock(){
// 一、獲取分布式鎖
String uuid = UUID.randomUUID().toString();
Boolean lock = stringRedisTemplate.opsForValue().setIfAbsent("lock", uuid,30,TimeUnit.SECONDS);

if (lock) {
System.out.println("獲取分布式鎖成功...");
Map> dataFromDb = null;
try {
//加鎖成功...執(zhí)行業(yè)務(wù)
dataFromDb = getDataFromDb();
} finally {
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";

//刪除鎖
stringRedisTemplate.execute(new DefaultRedisScript(script, Long.class), Arrays.asList("lock"), uuid);

}
//先去redis查詢下保證當(dāng)前的鎖是自己的
//獲取值對比,對比成功刪除=原子性 lua腳本解鎖
// String lockValue = stringRedisTemplate.opsForValue().get("lock");
// if (uuid.equals(lockValue)) {
// //刪除我自己的鎖
// stringRedisTemplate.delete("lock");
// }

return dataFromDb;
}else{
System.out.println("獲取分布式鎖失敗...等待重試...");
//加鎖失敗...重試機(jī)制
//休眠一百毫秒
try {
TimeUnit.MILLISECONDS.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
//自旋的方式
return getCatelogJsonWithRedisLock();
}
}

這也是分布式鎖的最終模式,需要保證兩個點(diǎn):加鎖【設(shè)置鎖+過期時間】和刪除鎖【判斷+刪除】原子性。


當(dāng)前名稱:深入了解redis分布式鎖
URL鏈接:http://www.dlmjj.cn/article/dhdoeij.html