教你如何使用redis分布式锁
文章目錄
- 一、redis客戶端實現
- 應用
- 1.利用set nx命令實現分布式鎖
- 2.利用分布式鎖命令 setnx
- 問題
- 1.為什么不直接調用jedis.del(key)方法而采用redis+lua實現?
- 2.上述兩種方式存在的問題?
- 3.根本原因分析
- 二、分布式場景Redission分布式鎖的使用
- 1.分布式鎖的特性
- 2.分布式鎖的應用場景
- 三、zookeeper分布式鎖和redis分布式的區別
- 1.對于 Redis 的分布式鎖而言,它有以下缺點:
- 2.對于 ZK 分布式鎖而言:
- 3.使用建議
- 4.參數對比
一、redis客戶端實現
應用
1.利用set nx命令實現分布式鎖
Jedis jedis = new Jedis("127.0.0.1", 6309);public boolean getLock(String lockKey, String requestId, int expireTime) {//NX:保證互斥性//hset 原子性操作 只要lockKey有效 則說明有進程在使用分布式鎖// key:lockKey value:requestId NX:僅在鍵不存在時設置鍵 EX:設置指定的到期時間(以秒為單位)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);//成功設置 進程down 永久有效 別的進程就無法獲得鎖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() + "用戶進來");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.為什么不直接調用jedis.del(key)方法而采用redis+lua實現?
由于jedis.del(key)方法是刪除當前key不會區別當前是哪個客戶端,而采用redis+lua方式只有當前獲得鎖的客戶端才有資格刪除。例如,線程A獲得分布式鎖,線程B調用jedis.del(key)方法會把線程A的鎖刪除掉。
2.上述兩種方式存在的問題?
3.根本原因分析
CAP(Consistent一致性、Available可用性、Partition分區)原則,三者只能選其二,因此在分布式場景下p不能舍棄,那么只能是AP、CP原則。
二、分布式場景Redission分布式鎖的使用
Redisson是架設在Redis基礎上的一個java駐內存數據網格。
Redission在基于NIO的Netty框架上,生成環境使用分布式鎖。
數據網格:是將空間上不均勻分布的數據,按一定方法(如滑動平均法、克里格法或其他適當的數值推算方法)歸算成規則網格中的代表值(趨勢值)的過程
Redis集群至少需要3個master節點,所以現在總共有6個節點,就只能是1master對應1slave這種方式,1master-2slave,redis集群需要9個節點,以此類推。
配置代碼:
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()//集群狀態掃描間隔時間,單位是毫秒.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對象private static Redisson redisson = RedissonManager.getRedisson();//加鎖public static boolean acquire(String lockName) {//聲明key對象String key = LOCK_TITLE + lockName;//獲取鎖對象RLock mylock = redisson.getLock(key);//加鎖,并且設置鎖過期時間3秒,防止死鎖的產生 uuid+threadIdmylock.lock(3, SECONDS);//加鎖成功return true;}//鎖的釋放public static void release(String lockName) {//必須是和加鎖時的同一個keyString key = LOCK_TITLE + lockName;//獲取所對象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.分布式鎖的特性
2.分布式鎖的應用場景
三、zookeeper分布式鎖和redis分布式的區別
徹底講清楚ZooKeeper分布式鎖的實現原理
zk分布式鎖的使用案例
1.對于 Redis 的分布式鎖而言,它有以下缺點:
但是另一方面使用 Redis 實現分布式鎖在很多企業中非常常見,而且大部分情況下都不會遇到所謂的“極端復雜場景”。
所以使用 Redis 作為分布式鎖也不失為一種好的方案,最重要的一點是 Redis 的性能很高,可以支撐高并發的獲取、釋放鎖操作。
2.對于 ZK 分布式鎖而言:
ZK 天生設計定位就是分布式協調,強一致性。鎖的模型健壯、簡單易用、適合做分布式鎖。
如果獲取不到鎖,只需要添加一個監聽器就可以了,不用一直輪詢,性能消耗較小。
但是 ZK 也有其缺點:如果有較多的客戶端頻繁的申請加鎖、釋放鎖,對于 ZK 集群的壓力會比較大。
3.使用建議
就個人而言的話,我比較推崇 ZK 實現的鎖:因為 Redis 是有可能存在隱患的,可能會導致數據不對的情況。但是,怎么選用要看具體在公司的場景了。
如果有 ZK 集群條件,優先選用 ZK 實現,但是如果說公司里面只有 Redis 集群,沒有條件搭建 ZK 集群。
那么其實用 Redis 來實現也可以,另外還可能是系統設計者考慮到了系統已經有 Redis,但是又不希望再次引入一些外部依賴的情況下,可以選用 Redis。這個是要系統設計者基于架構來考慮了。
4.參數對比
一個ap模型不適合強一致的場景 一個cp雖然適合,但是每次節點交互攜帶的數據會限制節點的數量。
zk寫都在leader,不適合做高并發的分布式鎖。
數據庫實現分布式鎖,性能太差。
具體采用何種,還需要根據自身業務場景去選擇 !!!
總結
以上是生活随笔為你收集整理的教你如何使用redis分布式锁的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: hive的SerDe序列化
- 下一篇: shell读取文件并且遍历输出