IT评测·应用市场-qidao123.com技术社区

标题: 基于Redis的分布式锁 [打印本页]

作者: 莱莱    时间: 2024-8-14 09:50
标题: 基于Redis的分布式锁
1.为什么利用分布式锁?

    分布式锁多数用在分布式场景中,假如是单机的话用jvm的锁就行了。分布式锁的原理就是利用redis的set nx多线程的互斥特性,在多线程场景中锁住对共享资源的访问。而且redis是基于内存存储的中间件,加锁解锁的性能都非常快。
2.实现

1.设置set NX假如键不存在就是存储,EX是10秒钟后过期也可以利用PX毫秒
  1. SET mykey myvalue NX EX 10
复制代码
2.java实现
  1. redisTemplate.opsForValue().setIfAbsent(key,"uid",10,TimeUnit.SECONDS);
  2. //底层是execute直接执行,也可以保证操作的原子性
  3. return (Boolean)this.execute((connection) -> {
  4.             return connection.set(rawKey, rawValue, expiration, SetOption.ifAbsent());
  5.         }, true);
复制代码
3.看门狗机制

引入开门狗机制重要是防止在线程持有锁的时候,代码没有执行完毕就由于锁到期开释了锁,导致共享资源被修改。
  1. RLock rLock = redissonClient.getLock(name);
  2. try {
  3.     /**
  4.      * waitTime 获取锁的最长等待时间
  5.      * leaseTime 持有锁的时间
  6.      * unit 单位
  7.      * */
  8.     //TODO 开启看门狗lease -1 自我理解看门狗的功能就是在超时后任务没有执行完成就续期,
  9.     // 如果没有看门狗并且设置了leaseTime 就是当前锁的失效时间了
  10.     Boolean bo =  rLock.tryLock(5,-1,TimeUnit.MINUTES);
  11.     if (!bo){
  12.         System.out.println("没有拿到锁,遗憾离场:"+name);
  13.        continue;
  14.     }else {
  15.         System.out.println("拿到了锁,并开始耗时:"+name);
  16.         Thread.sleep(600);
  17.     }
  18. } catch (InterruptedException e) {
  19.     e.printStackTrace();
  20. } finally {
  21.     if (rLock.isHeldByCurrentThread()){
  22.         rLock.unlock();
  23.         System.out.println("释放了锁:"+name);
  24.     } else {
  25.         System.out.println("遗憾离场,并来到了finally:"+name);
  26.     }
  27. }
复制代码
  1. //底层逻辑 开一个监听线程看执行完毕删除锁,未执行完毕就加过期时间
  2. Long ttl = this.tryAcquire(leaseTime, unit, threadId);
  3. //判断是否开启了看门狗机制
  4. private RFuture<Boolean> tryAcquireOnceAsync(long leaseTime, TimeUnit unit, final long threadId) {
  5.     if (leaseTime != -1L) {
  6.         return this.tryLockInnerAsync(leaseTime, unit, threadId,
  7.                 RedisCommands.EVAL_NULL_BOOLEAN);
  8.     } else {
  9.         //延期时间默认30毫秒  this.lockWatchdogTimeout = 30000L;
  10.         RFuture<Boolean> ttlRemainingFuture = this.tryLockInnerAsync(this.commandExecutor.
  11.                 getConnectionManager().getCfg().getLockWatchdogTimeout(),
  12.                 TimeUnit.MILLISECONDS, threadId, RedisCommands.EVAL_NULL_BOOLEAN);
  13.         //监听这个线程时间
  14.         ttlRemainingFuture.addListener(new FutureListener<Boolean>() {
  15.             public void operationComplete(Future<Boolean> future) throws Exception {
  16.                 if (future.isSuccess()) {
  17.                     Boolean ttlRemaining = (Boolean)future.getNow();
  18.                     if (ttlRemaining) {
  19.                         RedissonLock.this.scheduleExpirationRenewal(threadId);
  20.                     }
  21.                 }
  22.             }
  23.         });
  24.         return ttlRemainingFuture;
  25.     }
  26. }
  27. //线程的具体监听方法 锁不存在了就取消监听
  28. private void scheduleExpirationRenewal(final long threadId) {
  29.     if (!expirationRenewalMap.containsKey(this.getEntryName())) {
  30.         Timeout task = this.commandExecutor.getConnectionManager().newTimeout(new TimerTask() {
  31.             public void run(Timeout timeout) throws Exception {
  32.                 RFuture<Boolean> future = RedissonLock.this.commandExecutor.evalWriteAsync(RedissonLock.this.getName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN, "if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then redis.call('pexpire', KEYS[1], ARGV[1]); return 1; end; return 0;", Collections.singletonList(RedissonLock.this.getName()), new Object[]{RedissonLock.this.internalLockLeaseTime, RedissonLock.this.getLockName(threadId)});
  33.                 future.addListener(new FutureListener<Boolean>() {
  34.                     public void operationComplete(Future<Boolean> future) throws Exception {
  35.                         RedissonLock.expirationRenewalMap.remove(RedissonLock.this.getEntryName());
  36.                         if (!future.isSuccess()) {
  37.                             RedissonLock.log.error("Can't update lock " + RedissonLock.this.getName() + " expiration", future.cause());
  38.                         } else {
  39.                             if ((Boolean)future.getNow()) {
  40.                                 RedissonLock.this.scheduleExpirationRenewal(threadId);
  41.                             }
  42.                         }
  43.                     }
  44.                 });
  45.             }
  46.         }, this.internalLockLeaseTime / 3L, TimeUnit.MILLISECONDS);
  47.         if (expirationRenewalMap.putIfAbsent(this.getEntryName(), task) != null) {
  48.             task.cancel();
  49.         }
  50.     }
  51. }
复制代码


4.redLock

    redLock重要是为了防止在redis集群中,在一个节点加了锁,但在加锁后节点就挂掉了,导致之前加的锁失效,线程重复加锁的问题。
  1. RLock lock1 = redissonInstance1.getLock("lock1");
  2. RLock lock2 = redissonInstance2.getLock("lock2");
  3. RLock lock3 = redissonInstance3.getLock("lock3");
  4. RedissonRedLock lock = new RedissonRedLock(lock1, lock2, lock3);
  5. // 同时加锁:lock1 lock2 lock3
  6. // 红锁在大部分节点上加锁成功就算成功。
  7. lock.lock();
  8. ...
  9. lock.unlock();
复制代码
  但红锁也有很多问题
5.读写锁

  读写锁是一种并发控制机制,用于控制对共享资源的访问。它允很多个读利用同时举行,但写利用是互斥的。这样可以在包管数据一致性的同时,进步体系的并发性能。
  在Redis缓存中,我们可以将数据分为热点数据和非热点数据。热点数据是指访问频率较高的数据,而非热点数据访问频率较低。对于热点数据,我们可以接纳读写锁机制,以进步并发性能。
  1. RReadWriteLock readWriteLock =  redissonClient.getReadWriteLock(mobile);
  2. readWriteLock.readLock();
  3. readWriteLock.writeLock();
复制代码
 

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 IT评测·应用市场-qidao123.com技术社区 (https://dis.qidao123.com/) Powered by Discuz! X3.4