日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

教你如何使用redis分布式锁

發(fā)布時(shí)間:2024/8/23 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 教你如何使用redis分布式锁 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

文章目錄

  • 一、redis客戶端實(shí)現(xiàn)
    • 應(yīng)用
      • 1.利用set nx命令實(shí)現(xiàn)分布式鎖
      • 2.利用分布式鎖命令 setnx
    • 問題
      • 1.為什么不直接調(diào)用jedis.del(key)方法而采用redis+lua實(shí)現(xiàn)?
      • 2.上述兩種方式存在的問題?
      • 3.根本原因分析
  • 二、分布式場(chǎng)景Redission分布式鎖的使用
    • 1.分布式鎖的特性
    • 2.分布式鎖的應(yīng)用場(chǎng)景
  • 三、zookeeper分布式鎖和redis分布式的區(qū)別
    • 1.對(duì)于 Redis 的分布式鎖而言,它有以下缺點(diǎn):
    • 2.對(duì)于 ZK 分布式鎖而言:
    • 3.使用建議
    • 4.參數(shù)對(duì)比

一、redis客戶端實(shí)現(xiàn)

應(yīng)用

1.利用set nx命令實(shí)現(xiàn)分布式鎖

Jedis jedis = new Jedis("127.0.0.1", 6309);public boolean getLock(String lockKey, String requestId, int expireTime) {//NX:保證互斥性//hset 原子性操作 只要lockKey有效 則說明有進(jìn)程在使用分布式鎖// key:lockKey value:requestId NX:僅在鍵不存在時(shí)設(shè)置鍵 EX:設(shè)置指定的到期時(shí)間(以秒為單位)String result = jedis.set(lockKey, requestId, "NX", "EX", expireTime);if ("OK".equals(result)) {return true;}return false;}/*** 釋放分布式鎖* @param lockKey* @param requestId*/public void releaseLock(String lockKey,String requestId) {if (requestId.equals(jedis.get(lockKey))) {jedis.del(lockKey);}}

2.利用分布式鎖命令 setnx

public static boolean getLock2(Jedis jedis, String lockKey, String requestId, int expireTime) {Long result = jedis.setnx(lockKey, requestId);//成功設(shè)置 進(jìn)程down 永久有效 別的進(jìn)程就無法獲得鎖if(result == 1) {jedis.expire(lockKey, expireTime);return true;}return false;}public static boolean releaseLock2(Jedis jedis, String lockKey, String requestId) {String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";Object result = jedis.eval(script, Collections.singletonList(lockKey),Collections.singletonList(requestId));if (result.equals(1L)) {return true;}return false;}public static void main(String[] args) {String redisLock = "my_lock";ExecutorService executorService = Executors.newFixedThreadPool(10);for (int i = 0; i <100; i++){executorService.execute(() -> {Jedis jedis = new Jedis("127.0.0.1", 6379);UUID uuid = UUID.randomUUID();System.out.println(uuid.toString() + "用戶進(jìn)來");boolean isLock = getLock2(jedis, redisLock, uuid.toString(), 30000);if (isLock){System.out.println(uuid.toString() + "用戶嘗試獲得鎖成功!");releaseLock2(jedis, redisLock, uuid.toString());System.out.println(uuid.toString() + "用戶釋放鎖");}else{System.out.println(uuid.toString() + "用戶獲得鎖失敗");}});}}

問題

1.為什么不直接調(diào)用jedis.del(key)方法而采用redis+lua實(shí)現(xiàn)?

由于jedis.del(key)方法是刪除當(dāng)前key不會(huì)區(qū)別當(dāng)前是哪個(gè)客戶端,而采用redis+lua方式只有當(dāng)前獲得鎖的客戶端才有資格刪除。例如,線程A獲得分布式鎖,線程B調(diào)用jedis.del(key)方法會(huì)把線程A的鎖刪除掉。

2.上述兩種方式存在的問題?

  • 單機(jī),無法保證高可用
  • 主從,無法保證數(shù)據(jù)強(qiáng)一致性,會(huì)去從庫(kù)重復(fù)獲得鎖
  • 無法續(xù)租,超過expireTime后,不能繼續(xù)使用
  • 3.根本原因分析

    CAP(Consistent一致性、Available可用性、Partition分區(qū))原則,三者只能選其二,因此在分布式場(chǎng)景下p不能舍棄,那么只能是AP、CP原則。

    二、分布式場(chǎng)景Redission分布式鎖的使用

    Redisson是架設(shè)在Redis基礎(chǔ)上的一個(gè)java駐內(nèi)存數(shù)據(jù)網(wǎng)格。
    Redission在基于NIO的Netty框架上,生成環(huán)境使用分布式鎖。

    數(shù)據(jù)網(wǎng)格:是將空間上不均勻分布的數(shù)據(jù),按一定方法(如滑動(dòng)平均法、克里格法或其他適當(dāng)?shù)臄?shù)值推算方法)歸算成規(guī)則網(wǎng)格中的代表值(趨勢(shì)值)的過程

    Redis集群至少需要3個(gè)master節(jié)點(diǎn),所以現(xiàn)在總共有6個(gè)節(jié)點(diǎn),就只能是1master對(duì)應(yīng)1slave這種方式,1master-2slave,redis集群需要9個(gè)節(jié)點(diǎn),以此類推。

    配置代碼:

    package com.learn.cache;import org.redisson.Redisson; import org.redisson.config.Config;public class RedissonManager {private static Config config = new Config();private static Redisson redisson = null;static {config.useClusterServers()//集群狀態(tài)掃描間隔時(shí)間,單位是毫秒.setScanInterval(2000).addNodeAddress("redis://127.0.0.1:7001").addNodeAddress("redis://127.0.0.1:7002").addNodeAddress("redis://127.0.0.1:7003").addNodeAddress("redis://127.0.0.1:7004").addNodeAddress("redis://127.0.0.1:7005").addNodeAddress("redis://127.0.0.1:7006");redisson = (Redisson) Redisson.create(config);}public static Redisson getRedisson() {return redisson;}}

    使用demo:

    package com.learn.cache;import org.redisson.Redisson; import org.redisson.api.RLock;import java.util.UUID; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors;import static java.util.concurrent.TimeUnit.SECONDS;public class DistributedRedisLock {private static final String LOCK_TITLE = "redisLock_";//從配置類中獲取redisson對(duì)象private static Redisson redisson = RedissonManager.getRedisson();//加鎖public static boolean acquire(String lockName) {//聲明key對(duì)象String key = LOCK_TITLE + lockName;//獲取鎖對(duì)象RLock mylock = redisson.getLock(key);//加鎖,并且設(shè)置鎖過期時(shí)間3秒,防止死鎖的產(chǎn)生 uuid+threadIdmylock.lock(3, SECONDS);//加鎖成功return true;}//鎖的釋放public static void release(String lockName) {//必須是和加鎖時(shí)的同一個(gè)keyString key = LOCK_TITLE + lockName;//獲取所對(duì)象RLock mylock = redisson.getLock(key);//釋放鎖(解鎖)mylock.unlock();}public static void main(String[] args) {String key = "lock001";ExecutorService executorService = Executors.newFixedThreadPool(20);for (int i = 0; i < 100; i++){executorService.execute(() -> {//加鎖boolean acquire = acquire(key);String uuid = UUID.randomUUID().toString();if (acquire){System.out.println(uuid + "用戶獲得鎖成功");release(key);System.out.println(uuid + "用戶釋放鎖");}else{System.out.println(uuid + "用戶獲得鎖失敗");}});}}}

    1.分布式鎖的特性

  • 互斥性:任意時(shí)刻保持只能有一個(gè)客戶端獲得鎖
  • 同一性:鎖只能被同一個(gè)客戶端刪除,不能被其他客戶端刪除,同上jedis.setnx(key)不能利用jedis.del(key)刪除
  • 可重入性:持有該鎖的客戶端可重復(fù)獲得鎖
  • 容錯(cuò)性:鎖對(duì)象由于服務(wù)掛掉,會(huì)自動(dòng)釋放鎖,防止死鎖
  • 2.分布式鎖的應(yīng)用場(chǎng)景

  • 搶紅包、秒殺下單場(chǎng)景等,由于紅包、庫(kù)存的數(shù)量是確定的,如果此時(shí)同時(shí)有多個(gè)用戶搶紅包、下訂單,此時(shí)如果不利用分布式鎖處理的話,那么會(huì)出現(xiàn)并發(fā)問題。
  • 詳細(xì)應(yīng)用場(chǎng)景可參考: 文章
  • 三、zookeeper分布式鎖和redis分布式的區(qū)別

    徹底講清楚ZooKeeper分布式鎖的實(shí)現(xiàn)原理

    zk分布式鎖的使用案例

    1.對(duì)于 Redis 的分布式鎖而言,它有以下缺點(diǎn):

  • 它獲取鎖的方式簡(jiǎn)單粗暴,獲取不到鎖直接不斷嘗試獲取鎖,比較消耗性能。
  • 另外來說的話,Redis的設(shè)計(jì)定位決定了它的數(shù)據(jù)并不是強(qiáng)一致性的,在某些極端情況下,可能會(huì)出現(xiàn)問題。鎖的模型不夠健壯。 即便使用 Redlock算法來實(shí)現(xiàn),在某些復(fù)雜場(chǎng)景下,也無法保證其實(shí)現(xiàn) 100% 沒有問題,關(guān)于 Redlock 的討論可以看 How to do distributed locking。
  • 但是另一方面使用 Redis 實(shí)現(xiàn)分布式鎖在很多企業(yè)中非常常見,而且大部分情況下都不會(huì)遇到所謂的“極端復(fù)雜場(chǎng)景”。

    所以使用 Redis 作為分布式鎖也不失為一種好的方案,最重要的一點(diǎn)是 Redis 的性能很高,可以支撐高并發(fā)的獲取、釋放鎖操作。

    2.對(duì)于 ZK 分布式鎖而言:

    ZK 天生設(shè)計(jì)定位就是分布式協(xié)調(diào),強(qiáng)一致性。鎖的模型健壯、簡(jiǎn)單易用、適合做分布式鎖。
    如果獲取不到鎖,只需要添加一個(gè)監(jiān)聽器就可以了,不用一直輪詢,性能消耗較小。

    但是 ZK 也有其缺點(diǎn):如果有較多的客戶端頻繁的申請(qǐng)加鎖、釋放鎖,對(duì)于 ZK 集群的壓力會(huì)比較大

    3.使用建議

    就個(gè)人而言的話,我比較推崇 ZK 實(shí)現(xiàn)的鎖:因?yàn)?Redis 是有可能存在隱患的,可能會(huì)導(dǎo)致數(shù)據(jù)不對(duì)的情況。但是,怎么選用要看具體在公司的場(chǎng)景了。

    如果有 ZK 集群條件,優(yōu)先選用 ZK 實(shí)現(xiàn),但是如果說公司里面只有 Redis 集群,沒有條件搭建 ZK 集群。

    那么其實(shí)用 Redis 來實(shí)現(xiàn)也可以,另外還可能是系統(tǒng)設(shè)計(jì)者考慮到了系統(tǒng)已經(jīng)有 Redis,但是又不希望再次引入一些外部依賴的情況下,可以選用 Redis。這個(gè)是要系統(tǒng)設(shè)計(jì)者基于架構(gòu)來考慮了。

    4.參數(shù)對(duì)比


    一個(gè)ap模型不適合強(qiáng)一致的場(chǎng)景 一個(gè)cp雖然適合,但是每次節(jié)點(diǎn)交互攜帶的數(shù)據(jù)會(huì)限制節(jié)點(diǎn)的數(shù)量。

    zk寫都在leader,不適合做高并發(fā)的分布式鎖。

    數(shù)據(jù)庫(kù)實(shí)現(xiàn)分布式鎖,性能太差。

    具體采用何種,還需要根據(jù)自身業(yè)務(wù)場(chǎng)景去選擇 !!!

    總結(jié)

    以上是生活随笔為你收集整理的教你如何使用redis分布式锁的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。