激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

服務器之家:專注于服務器技術及軟件下載分享
分類導航

Mysql|Sql Server|Oracle|Redis|MongoDB|PostgreSQL|Sqlite|DB2|mariadb|Access|數據庫技術|

服務器之家 - 數據庫 - Redis - Redis分布式鎖的實現方式(redis面試題)

Redis分布式鎖的實現方式(redis面試題)

2020-03-01 14:24程序員之道 Redis

這篇文章主要介紹了Redis分布式鎖的實現方式(面試常見),需要的朋友可以參考下

 

什么是分布式鎖?

要介紹分布式鎖,首先要提到與分布式鎖相對應的是線程鎖、進程鎖。

線程鎖:主要用來給方法、代碼塊加鎖。當某個方法或代碼使用鎖,在同一時刻僅有一個線程執行該方法或該代碼段。線程鎖只在同一JVM中有效果,因為線程鎖的實現在根本上是依靠線程之間共享內存實現的,比如synchronized是共享對象頭,顯示鎖Lock是共享某個變量(state)。

進程鎖:為了控制同一操作系統中多個進程訪問某個共享資源,因為進程具有獨立性,各個進程無法訪問其他進程的資源,因此無法通過synchronized等線程鎖實現進程鎖。

分布式鎖:當多個進程不在同一個系統中,用分布式鎖控制多個進程對資源的訪問。

 

前言

現在的業務場景越來越復雜,使用的架構也就越來越復雜,分布式、高并發已經是業務要求的常態。像騰訊系的不少服務,還有CDN優化、異地多備份等處理。

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

 

實現要點

  1. 互斥性,同一時刻,智能有一個客戶端持有鎖。
  2. 防止死鎖發生,如果持有鎖的客戶端崩潰沒有主動釋放鎖,也要保證鎖可以正常釋放及其他客戶端可以正常加鎖。
  3. 加鎖和釋放鎖必須是同一個客戶端。
  4. 容錯性,只有redis還有節點存活,就可以進行正常的加鎖解鎖操作。
 

正確的redis分布式鎖實現

 

錯誤加鎖方式

錯誤方式一

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

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

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

錯誤方式二

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public static boolean wrongLock(Jedis jedis, String key, int expireTime) {
  long expireTs = System.currentTimeMillis() + expireTime;
  // 鎖不存在,當前線程加鎖成果
  if (jedis.setnx(key, String.valueOf(expireTs)) == 1) {
   return true;
  }
 
  String value = jedis.get(key);
  //如果當前鎖存在,且鎖已過期
  if (value != null && NumberUtils.toLong(value) < System.currentTimeMillis()) {
   //鎖過期,設置新的過期時間
   String oldValue = jedis.getSet(key, String.valueOf(expireTs));
   if (oldValue != null && oldValue.equals(value)) {
    // 多線程并發下,只有一個線程會設置成功
    // 設置成功的這個線程,key的舊值一定和設置之前的key的值一致
    return true;
   }
  }
  // 其他情況,加鎖失敗
  return true;
 }

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

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

鎖過期時,使用 jedis.getSet雖然可以保證只有一個線程設置成功,但是不能保證加鎖和解鎖為同一個客戶端,因為沒有標志鎖是哪個客戶端設置的嘛。

 

錯誤解鎖方式

解鎖錯誤方式一

直接刪除key

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

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

解鎖錯誤方式二

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

?
1
2
3
4
5
6
public static void wrongReleaseLock(Jedis jedis, String key, String uniqueId) {
 if (uniqueId.equals(jedis.get(key))) {
  // 如果這時鎖過期自動釋放,又被其他線程加鎖,該線程就會釋放不屬于自己的鎖
  jedis.del(key);
 }
}

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

 

正確加鎖釋放鎖方式

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

命令必須保證互斥

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

value使用唯一id標志每個客戶端,保證只有鎖的持有者才可以釋放鎖

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

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

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
@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";
 // 鎖的超時時間
 private static int EXPIRE_TIME = 5 * 1000;
 // 鎖等待時間
 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 {
   // 超過等待時間,加鎖失敗
   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必須是一個原子操作
  // 且誰擁有鎖,誰釋放
  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;
 }
}

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

總結

以上所述是小編給大家介紹的Redis分布式鎖的實現方式(redis面試題),希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對服務器之家網站的支持!如果你覺得本文對你有幫助,歡迎轉載,煩請注明出處,謝謝!

原文鏈接:https://blog.csdn.net/chanllenge/article/details/102983597

延伸 · 閱讀

精彩推薦
  • RedisRedis如何實現數據庫讀寫分離詳解

    Redis如何實現數據庫讀寫分離詳解

    Redis的主從架構,能幫助我們實現讀多,寫少的情況,下面這篇文章主要給大家介紹了關于Redis如何實現數據庫讀寫分離的相關資料,文中通過示例代碼介紹...

    羅兵漂流記6092019-11-11
  • Redisredis 交集、并集、差集的具體使用

    redis 交集、并集、差集的具體使用

    這篇文章主要介紹了redis 交集、并集、差集的具體使用,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友...

    xiaojin21cen10152021-07-27
  • RedisRedis的配置、啟動、操作和關閉方法

    Redis的配置、啟動、操作和關閉方法

    今天小編就為大家分享一篇Redis的配置、啟動、操作和關閉方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧 ...

    大道化簡5312019-11-14
  • Redisredis中如何使用lua腳本讓你的靈活性提高5個逼格詳解

    redis中如何使用lua腳本讓你的靈活性提高5個逼格詳解

    這篇文章主要給大家介紹了關于redis中如何使用lua腳本讓你的靈活性提高5個逼格的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具...

    一線碼農5812019-11-18
  • RedisRedis全量復制與部分復制示例詳解

    Redis全量復制與部分復制示例詳解

    這篇文章主要給大家介紹了關于Redis全量復制與部分復制的相關資料,文中通過示例代碼介紹的非常詳細,對大家學習或者使用Redis爬蟲具有一定的參考學習...

    豆子先生5052019-11-27
  • Redisredis實現排行榜功能

    redis實現排行榜功能

    排行榜在很多地方都能使用到,redis的zset可以很方便地用來實現排行榜功能,本文就來簡單的介紹一下如何使用,具有一定的參考價值,感興趣的小伙伴們...

    乘月歸5022021-08-05
  • Redis詳解Redis復制原理

    詳解Redis復制原理

    與大多數db一樣,Redis也提供了復制機制,以滿足故障恢復和負載均衡等需求。復制也是Redis高可用的基礎,哨兵和集群都是建立在復制基礎上實現高可用的...

    李留廣10222021-08-09
  • RedisRedis 事務知識點相關總結

    Redis 事務知識點相關總結

    這篇文章主要介紹了Redis 事務相關總結,幫助大家更好的理解和學習使用Redis,感興趣的朋友可以了解下...

    AsiaYe8232021-07-28
主站蜘蛛池模板: 亚洲午夜在线 | 欧美一区中文字幕 | 久久国产精品电影 | 国产在线观看一区二区三区 | 国产精品一区二区三区在线播放 | 中文字幕在线播放第一页 | 国内精品一级毛片免费看 | videos韩国 | 美国黄色毛片女人性生活片 | 欧美在线观看视频一区二区 | 久久国产91 | 干一夜综合 | 少妇淫片免费一级毛片 | 深夜免费观看视频 | 永久免费不卡在线观看黄网站 | 国内精品久久久久久久久久久久 | 91成人午夜性a一级毛片 | 久久精品国产精品亚洲 | 成人福利免费在线观看 | 免费一区二区三区 | 欧美精品成人一区二区三区四区 | 本站只有精品 | 狠狠色噜噜狠狠狠米奇9999 | 国产精品69久久 | 舌头伸进添的我好爽高潮网站 | 视频一区 中文字幕 | 91成人久久 | 欧美熟videos肥婆 | 午夜天堂在线 | 欧美一区中文字幕 | 91在线免费观看 | 美女视频在线观看黄 | 国产手机在线视频 | 综合网天天色 | 欧美精品成人 | 99精品视频在线导航 | 7777视频| 桥本有菜免费av一区二区三区 | 亚洲一区二区三区在线免费观看 | 欧美日韩亚洲国产 | 高清国产午夜精品久久久久久 |