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

Redis分布式鎖的實(shí)現(xiàn)方式(redis面試題)

 更新時(shí)間:2020年01月20日 09:41:36   作者:程序員之道  
這篇文章主要介紹了Redis分布式鎖的實(shí)現(xiàn)方式(面試常見),需要的朋友可以參考下

什么是分布式鎖?

要介紹分布式鎖,首先要提到與分布式鎖相對(duì)應(yīng)的是線程鎖、進(jìn)程鎖。

線程鎖:主要用來給方法、代碼塊加鎖。當(dāng)某個(gè)方法或代碼使用鎖,在同一時(shí)刻僅有一個(gè)線程執(zhí)行該方法或該代碼段。線程鎖只在同一JVM中有效果,因?yàn)榫€程鎖的實(shí)現(xiàn)在根本上是依靠線程之間共享內(nèi)存實(shí)現(xiàn)的,比如synchronized是共享對(duì)象頭,顯示鎖Lock是共享某個(gè)變量(state)。

進(jìn)程鎖:為了控制同一操作系統(tǒng)中多個(gè)進(jìn)程訪問某個(gè)共享資源,因?yàn)檫M(jìn)程具有獨(dú)立性,各個(gè)進(jìn)程無法訪問其他進(jìn)程的資源,因此無法通過synchronized等線程鎖實(shí)現(xiàn)進(jìn)程鎖。

分布式鎖:當(dāng)多個(gè)進(jìn)程不在同一個(gè)系統(tǒng)中,用分布式鎖控制多個(gè)進(jìn)程對(duì)資源的訪問。

前言

現(xiàn)在的業(yè)務(wù)場(chǎng)景越來越復(fù)雜,使用的架構(gòu)也就越來越復(fù)雜,分布式、高并發(fā)已經(jīng)是業(yè)務(wù)要求的常態(tài)。像騰訊系的不少服務(wù),還有CDN優(yōu)化、異地多備份等處理。

說到分布式,就必然涉及到分布式鎖的概念,如何保證不同機(jī)器不同線程的分布式鎖同步呢?

實(shí)現(xiàn)要點(diǎn)

  1. 互斥性,同一時(shí)刻,智能有一個(gè)客戶端持有鎖。
  2. 防止死鎖發(fā)生,如果持有鎖的客戶端崩潰沒有主動(dòng)釋放鎖,也要保證鎖可以正常釋放及其他客戶端可以正常加鎖。
  3. 加鎖和釋放鎖必須是同一個(gè)客戶端。
  4. 容錯(cuò)性,只有redis還有節(jié)點(diǎn)存活,就可以進(jìn)行正常的加鎖解鎖操作。

正確的redis分布式鎖實(shí)現(xiàn)

錯(cuò)誤加鎖方式

錯(cuò)誤方式一

保證互斥和防止死鎖,首先想到的使用redis的setnx命令保證互斥,為了防止死鎖,鎖需要設(shè)置一個(gè)超時(shí)時(shí)間。

 public static void wrongLock(Jedis jedis, String key, String uniqueId, int expireTime) {
  Long result = jedis.setnx(key, uniqueId);
  if (1 == result) {
   //如果該redis實(shí)例崩潰,那就無法設(shè)置過期時(shí)間了
   jedis.expire(key, expireTime);
  }
 }

在多線程并發(fā)環(huán)境下,任何非原子性的操作,都可能導(dǎo)致問題。這段代碼中,如果設(shè)置過期時(shí)間時(shí),redis實(shí)例崩潰,就無法設(shè)置過期時(shí)間。如果客戶端沒有正確的釋放鎖,那么該鎖(永遠(yuǎn)不會(huì)過期),就永遠(yuǎn)不會(huì)被釋放。

錯(cuò)誤方式二

比較容易想到的就是設(shè)置值和超時(shí)時(shí)間為原子原子操作就可以解決問題。那使用setnx命令,將value設(shè)置為過期時(shí)間不就ok了嗎?

public static boolean wrongLock(Jedis jedis, String key, int expireTime) {
  long expireTs = System.currentTimeMillis() + expireTime;
  // 鎖不存在,當(dāng)前線程加鎖成果
  if (jedis.setnx(key, String.valueOf(expireTs)) == 1) {
   return true;
  }

  String value = jedis.get(key);
  //如果當(dāng)前鎖存在,且鎖已過期
  if (value != null && NumberUtils.toLong(value) < System.currentTimeMillis()) {
   //鎖過期,設(shè)置新的過期時(shí)間
   String oldValue = jedis.getSet(key, String.valueOf(expireTs));
   if (oldValue != null && oldValue.equals(value)) {
    // 多線程并發(fā)下,只有一個(gè)線程會(huì)設(shè)置成功
    // 設(shè)置成功的這個(gè)線程,key的舊值一定和設(shè)置之前的key的值一致
    return true;
   }
  }
  // 其他情況,加鎖失敗
  return true;
 }

乍看之下,沒有什么問題。但仔細(xì)分析,有如下問題:

value設(shè)置為過期時(shí)間,就要求各個(gè)客戶端嚴(yán)格的時(shí)鐘同步,這就需要使用到同步時(shí)鐘。即使有同步時(shí)鐘,分布式的服務(wù)器一般來說時(shí)間肯定是存在少許誤差的。

鎖過期時(shí),使用 jedis.getSet雖然可以保證只有一個(gè)線程設(shè)置成功,但是不能保證加鎖和解鎖為同一個(gè)客戶端,因?yàn)闆]有標(biāo)志鎖是哪個(gè)客戶端設(shè)置的嘛。

錯(cuò)誤解鎖方式

解鎖錯(cuò)誤方式一

直接刪除key

public static void wrongReleaseLock(Jedis jedis, String key) {
  //不是自己加鎖的key,也會(huì)被釋放
  jedis.del(key);
 }

簡(jiǎn)單粗暴,直接解鎖,但是不是自己加鎖的,也會(huì)被刪除,這好像有點(diǎn)太隨意了吧!

解鎖錯(cuò)誤方式二

判斷自己是不是鎖的持有者,如果是,則只有持有者才可以釋放鎖。

 public static void wrongReleaseLock(Jedis jedis, String key, String uniqueId) {
  if (uniqueId.equals(jedis.get(key))) {
   // 如果這時(shí)鎖過期自動(dòng)釋放,又被其他線程加鎖,該線程就會(huì)釋放不屬于自己的鎖
   jedis.del(key);
  }
 }

看起來很完美啊,但是如果你判斷的時(shí)候鎖是自己持有的,這時(shí)鎖超時(shí)自動(dòng)釋放了。然后又被其他客戶端重新上鎖,然后當(dāng)前線程執(zhí)行到j(luò)edis.del(key),這樣這個(gè)線程不就刪除了其他線程上的鎖嘛,好像有點(diǎn)亂套了哦!

正確加鎖釋放鎖方式

基本上避免了以上幾種錯(cuò)誤方式之外,就是正確的方式了。要滿足以下幾個(gè)條件:

命令必須保證互斥

設(shè)置的key必須要有過期時(shí)間,防止崩潰時(shí)鎖無法釋放

value使用唯一id標(biāo)志每個(gè)客戶端,保證只有鎖的持有者才可以釋放鎖

加鎖直接使用set命令同時(shí)設(shè)置唯一id和過期時(shí)間;其中解鎖稍微復(fù)雜些,加鎖之后可以返回唯一id,標(biāo)志此鎖是該客戶端鎖擁有;釋放鎖時(shí)要先判斷擁有者是否是自己,然后刪除,這個(gè)需要redis的lua腳本保證兩個(gè)命令的原子性執(zhí)行。

下面是具體的加鎖和釋放鎖的代碼:

@Slf4j
public class RedisDistributedLock {
 private static final String LOCK_SUCCESS = "OK";
 private static final Long RELEASE_SUCCESS = 1L;
 private static final String SET_IF_NOT_EXIST = "NX";
 private static final String SET_WITH_EXPIRE_TIME = "PX";
 // 鎖的超時(shí)時(shí)間
 private static int EXPIRE_TIME = 5 * 1000;
 // 鎖等待時(shí)間
 private static int WAIT_TIME = 1 * 1000;
 private Jedis jedis;
 private String key;
 public RedisDistributedLock(Jedis jedis, String key) {
  this.jedis = jedis;
  this.key = key;
 }
 // 不斷嘗試加鎖
 public String lock() {
  try {
   // 超過等待時(shí)間,加鎖失敗
   long waitEnd = System.currentTimeMillis() + WAIT_TIME;
   String value = UUID.randomUUID().toString();
   while (System.currentTimeMillis() < waitEnd) {
    String result = jedis.set(key, value, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, EXPIRE_TIME);
    if (LOCK_SUCCESS.equals(result)) {
     return value;
    }
    try {
     Thread.sleep(10);
    } catch (InterruptedException e) {
     Thread.currentThread().interrupt();
    }
   }
  } catch (Exception ex) {
   log.error("lock error", ex);
  }
  return null;
 }
 public boolean release(String value) {
  if (value == null) {
   return false;
  }
  // 判斷key存在并且刪除key必須是一個(gè)原子操作
  // 且誰(shuí)擁有鎖,誰(shuí)釋放
  String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
  Object result = new Object();
  try {
   result = jedis.eval(script, Collections.singletonList(key),
     Collections.singletonList(value));
   if (RELEASE_SUCCESS.equals(result)) {
    log.info("release lock success, value:{}", value);
    return true;
   }
  } catch (Exception e) {
   log.error("release lock error", e);
  } finally {
   if (jedis != null) {
    jedis.close();
   }
  }
  log.info("release lock failed, value:{}, result:{}", value, result);
  return false;
 }
}

單是一個(gè)redis的分布式鎖就有這么多道道,不知道你是否看明白了?留言討論下吧!

總結(jié)

以上所述是小編給大家介紹的Redis分布式鎖的實(shí)現(xiàn)方式(redis面試題),希望對(duì)大家有所幫助,如果大家有任何疑問請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!如果你覺得本文對(duì)你有幫助,歡迎轉(zhuǎn)載,煩請(qǐng)注明出處,謝謝!

相關(guān)文章

  • redis.clients.jedis.exceptions.JedisBusyException無法處理異常的解決方法

    redis.clients.jedis.exceptions.JedisBusyException無法處理異常的解決方法

    redis.clients.jedis.exceptions.JedisBusyException異常通常不是 Jedis客戶端直接拋出的標(biāo)準(zhǔn)異常,本文就來介紹一下異常的解決方法,感興趣的可以了解一下
    2024-05-05
  • Redis通過scan查找不過期的 key(方法詳解)

    Redis通過scan查找不過期的 key(方法詳解)

    SCAN 命令是一個(gè)基于游標(biāo)的迭代器,每次被調(diào)用之后, 都會(huì)向用戶返回一個(gè)新的游標(biāo), 用戶在下次迭代時(shí)需要使用這個(gè)新游標(biāo)作為 SCAN 命令的游標(biāo)參數(shù), 以此來延續(xù)之前的迭代過程,對(duì)Redis scan 查找 key相關(guān)知識(shí)感興趣的朋友一起看看吧
    2021-08-08
  • Redis的5種數(shù)據(jù)類型與常用命令講解

    Redis的5種數(shù)據(jù)類型與常用命令講解

    今天小編就為大家分享一篇關(guān)于Redis的5種數(shù)據(jù)類型與常用命令講解,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧
    2019-03-03
  • Redis之BigKey與HotKey問題詳解

    Redis之BigKey與HotKey問題詳解

    在Redis的實(shí)際使用過程中,BigKey和 HotKey是兩個(gè)常見且可能導(dǎo)致嚴(yán)重性能問題的問題,它們可能導(dǎo)致 Redis 實(shí)例響應(yīng)變慢、內(nèi)存使用不均、甚至服務(wù)不可用,下面就來介紹一下這兩個(gè)的問題
    2026-02-02
  • redis和hiredis的基本使用詳解

    redis和hiredis的基本使用詳解

    這篇文章主要介紹了redis和hiredis的基本使用,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2025-05-05
  • Redis Sentinel實(shí)現(xiàn)高可用配置的詳細(xì)步驟

    Redis Sentinel實(shí)現(xiàn)高可用配置的詳細(xì)步驟

    這篇文章主要介紹了Redis Sentinel實(shí)現(xiàn)高可用配置的詳細(xì)步驟,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-09-09
  • 通俗易懂的Redis數(shù)據(jù)結(jié)構(gòu)基礎(chǔ)教程(入門)

    通俗易懂的Redis數(shù)據(jù)結(jié)構(gòu)基礎(chǔ)教程(入門)

    這篇文章主要介紹了通俗易懂的Redis數(shù)據(jù)結(jié)構(gòu)基礎(chǔ)教程,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-03-03
  • Redis實(shí)現(xiàn)集群搭建+集群讀寫的示例

    Redis實(shí)現(xiàn)集群搭建+集群讀寫的示例

    本文介紹了Redis集群的搭建和讀寫操作,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2025-02-02
  • redis數(shù)據(jù)類型及應(yīng)用場(chǎng)景知識(shí)點(diǎn)總結(jié)

    redis數(shù)據(jù)類型及應(yīng)用場(chǎng)景知識(shí)點(diǎn)總結(jié)

    在本篇文章里小編給大家整理的是關(guān)于
    2020-02-02
  • Redis MCP 客戶端應(yīng)用配置的實(shí)現(xiàn)示例

    Redis MCP 客戶端應(yīng)用配置的實(shí)現(xiàn)示例

    本文詳細(xì)介紹配置客戶端應(yīng)用使用Redis MCP服務(wù),客戶端配置包括自動(dòng)配置和手動(dòng)配置兩種方式,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2025-11-11

最新評(píng)論

北流市| 卢氏县| 浦北县| 保亭| 濮阳市| 陆丰市| 红安县| 台中县| 巴林右旗| 兴国县| 芦溪县| 连云港市| 宁乡县| 平阳县| 枣庄市| 哈尔滨市| 分宜县| 沿河| 信宜市| 台州市| 安庆市| 临夏县| 天峨县| 泉州市| 红桥区| 康马县| 庆云县| 琼结县| 东山县| 犍为县| 德州市| 白河县| 安丘市| 清远市| 娱乐| 仪征市| 石棉县| 锡林浩特市| 松溪县| 册亨县| 深州市|