设为首页收藏本站
网站公告 | 这是第一条公告
     

 找回密码
 立即注册
缓存时间01 现在时间01 缓存数据 当你走完一段之后回头看,你会发现,那些真正能被记得的事真的是没有多少,真正无法忘记的人屈指可数,真正有趣的日子不过是那么一些,而真正需要害怕的也是寥寥无几。

当你走完一段之后回头看,你会发现,那些真正能被记得的事真的是没有多少,真正无法忘记的人屈指可数,真正有趣的日子不过是那么一些,而真正需要害怕的也是寥寥无几。

查看: 239|回复: 0

Redis分布式锁的几种实现方法

[复制链接]

  离线 

TA的专栏

  • 打卡等级:即来则安
  • 打卡总天数:20
  • 打卡月天数:0
  • 打卡总奖励:231
  • 最近打卡:2025-10-23 08:57:19
等级头衔

等級:晓枫资讯-上等兵

在线时间
0 小时

积分成就
威望
0
贡献
320
主题
288
精华
0
金钱
1161
积分
648
注册时间
2023-2-11
最后登录
2025-10-23

发表于 2025-9-1 06:13:04 | 显示全部楼层 |阅读模式
Redis基本命令:
  1. // 设置键myKey的值为myValue,并且该键在10秒后过期
  2. SET myKey myValue EX 10
  3. // 设置键myKey的值为myValue,并且该键在1000毫秒(1秒)后过期
  4. SET myKey myValue PX 1000
  5. // 指定key过期时间,单位是秒,过期后自动删除
  6. EXPIRE key_name second_num
  7. // 指定key过期时间,单位是毫秒,过期后自动删除
  8. PEXPIRE key_name millisecond_num

  9. // 返回key过期时间
  10. TTL key_name

  11. SET key value         //设置键key的值为value
  12. SETNX key value         //只有在键key不存在的情况下,将key的值设置为value
  13. SETEX key seconds value         //将键key的值设置为value,并且超时时间为seconds秒
  14. PSETEX key milliseconds value  //将键key的值设置为value,并且超时时间为milliseconds毫秒
复制代码
一、基础方案:SETNX命令实现
  1. public class SimpleRedisLock {
  2.     private Jedis jedis;
  3.     private String lockKey;

  4.     public SimpleRedisLock(Jedis jedis, String lockKey) {
  5.         this.jedis = jedis;
  6.         this.lockKey = lockKey;
  7.     }
  8.     public boolean tryLock() {
  9.         Long result = jedis.setnx(lockKey, "locked");
  10.         if (result == 1) {
  11.             jedis.expire(lockKey, 30); // 设置过期时间
  12.             return true;
  13.         }
  14.         return false;
  15.     }
  16.     public void unlock() {
  17.         jedis.del(lockKey);
  18.     }
  19. }

  20. // 使用示例
  21. Jedis jedis = new Jedis("localhost");
  22. SimpleRedisLock lock = new SimpleRedisLock(jedis, "order_lock");
  23. try{
  24.     if(lock.tryLock()){
  25.     // 业务逻辑
  26.     }
  27. } finally {
  28.     lock.unlock();
  29. }
复制代码
问题分析:

  • 非原子操作:setnx和expire非原子操作,可能产生死锁
  • 锁误删:任何客户端都可以删除锁
  • 不可重入:同一线程重复获取会失败

二、改进方案:原子SET命令
  1. public class AtomicRedisLock {
  2.     private Jedis jedis;
  3.     private String lockKey;
  4.     private String clientId;

  5.     public SimpleRedisLock(Jedis jedis, String lockKey) {
  6.         this.jedis = jedis;
  7.         this.lockKey = lockKey;
  8.         this.clientId = UUID.randomUUID().toString();
  9.     }
  10.     public boolean tryLock(int expireSeconds) {
  11.         String result = jedis.set(lockKey, clientId, SetParams.setParams().nx().ex(expireSeconds));
  12.         return "OK".equals(result);
  13.     }
  14.     public boolean unlock() {
  15.         String script = "if redis.call('get', KEYS[1]) == ARGV[1] then " +
  16.                 "return redis.call('del', KEYS[1]) " +
  17.                 "else return 0 end";
  18.         Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(clientId));
  19.         return result.equals(1L);
  20.     }
  21. }

  22. // 使用示例
  23. Jedis jedis = new Jedis("localhost");
  24. AtomicRedisLock lock = new AtomicRedisLock(jedis, "payment_lock");
  25. try{
  26.     if(lock.tryLock(30)){
  27.     // 业务逻辑
  28.     }
  29. } finally {
  30.     lock.unlock();
  31. }
复制代码
核心改进:

  • 使用原子SET命令:SET key value NX EX
  • Lua脚本保证删除原子性
  • 客户端唯一标识防止误删
仍然存在的问题:

  • 锁续期困难
  • 单点故障风险
  • 业务超时可能导致锁失效

三、高可用方案:RedLock算法
  1. public class RedLock {
  2.     pprivate List<Jedis> jedisList;
  3.     private String lockKey;
  4.     private String clientId;
  5.     private int quorum;

  6.     public RedLock(List<Jedis> jedisList, String lockKey) {
  7.         this.jedisList = jedisList;
  8.         this.lockKey = lockKey;
  9.         this.clientId = UUID.randomUUID().toString();
  10.         this.quorum = jedisList.size() / 2 + 1;
  11.     }
  12.     public boolean tryLock(int expireMillis) {
  13.         long startTime = System.currentTimeMillis();
  14.         // 第一阶段:尝试获取多数节点锁
  15.         int successCount = 0;
  16.         for (Jedis jedis : jedisList) {
  17.             if (tryAcquire(jedis, expireMillis)) {
  18.                 successCount++;
  19.             }
  20.             if ((System.currentTimeMillis() - startTime) > expireMillis) {
  21.                 break;
  22.             }
  23.         }
  24.         // 第二阶段:验证锁有效性
  25.         if (successCount >= quorum) {
  26.             long validityTime = expireMillis - (System.currentTimeMillis() - startTime);
  27.             return validityTime > 0;
  28.         }
  29.         // 第三阶段:释放已获得的锁
  30.         for (Jedis jedis : jedisList) {
  31.             release(jedis);
  32.         }
  33.         return false;
  34.     }
  35.     private boolean tryAcquire(Jedis jedis, long expireMillis) {
  36.         try {
  37.             String result = jedis.set(lockKey, clientId, SetParams.setParams().nx().px(expireMillis));
  38.             return "OK".equals(result);
  39.         } catch (Exception e) {
  40.             return false;
  41.         }
  42.     }
  43.     private void release(Jedis jedis) {
  44.         String script = "if redis.call('get', KEYS[1]) == ARGV[1] then " +
  45.                 "return redis.call('del', KEYS[1]) else return 0 end";
  46.         jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(clientId));
  47.     }
  48. }
复制代码
部署要求:

  • 至少5个独立Redis实例
  • 节点间时钟同步
  • 需要配置合理的超时时间
适用场景:

  • 金融交易等对可靠性要求极高的场景
  • 需要跨机房部署的分布式系统

四、生产级方案:Redisson实现
  1. // 配置Redisson客户端
  2. Config config = new Config();
  3. config.useSingleServer().setAddress("redis://127.0.0.1:6379");
  4. RedissonClient redisson = Redisson.create(config);
  5. // 获取锁对象
  6. RLock lock = redisson.getLock("orderLock");
  7. try {
  8.     // 尝试加锁,最多等待100秒,锁定后30秒自动解锁
  9.     boolean isLock = lock.tryLock(100, 30, TimeUnit.SECONDS);
  10.     if (isLock) {
  11.         // 处理业务
  12.     }
  13. } finally {
  14.     lock.unlock();
  15. }

  16. // 关闭客户端
  17. redisson.shutdown();
复制代码
  1. // 自动续期机制(Watchdog),Watchdog实现原理(简化版)
  2. private void renewExpiration() {
  3.     Timeout task = commandExecutor.schedule(() -> {
  4.         if (redisClient.eval(...)){ // 检查是否仍持有锁
  5.             expireAsync(); // 续期
  6.             renewExpiration(); // 递归调用
  7.         }
  8.     }, internalLockLeaseTime / 3, TimeUnit.MILLISECONDS);
  9. }
复制代码
核心特性:

  • 支持可重入锁
  • 提供公平锁、联锁(MultiLock)、红锁(RedLock)实现
  • 完善的故障处理机制
到此这篇关于Redis分布式锁的几种实现方法的文章就介绍到这了,更多相关Redis分布式锁内容请搜索晓枫资讯以前的文章或继续浏览下面的相关文章希望大家以后多多支持晓枫资讯!

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
晓枫资讯-科技资讯社区-免责声明
免责声明:以上内容为本网站转自其它媒体,相关信息仅为传递更多信息之目的,不代表本网观点,亦不代表本网站赞同其观点或证实其内容的真实性。
      1、注册用户在本社区发表、转载的任何作品仅代表其个人观点,不代表本社区认同其观点。
      2、管理员及版主有权在不事先通知或不经作者准许的情况下删除其在本社区所发表的文章。
      3、本社区的文章部分内容可能来源于网络,仅供大家学习与参考,如有侵权,举报反馈:点击这里给我发消息进行删除处理。
      4、本社区一切资源不代表本站立场,并不代表本站赞同其观点和对其真实性负责。
      5、以上声明内容的最终解释权归《晓枫资讯-科技资讯社区》所有。
http://bbs.yzwlo.com 晓枫资讯--游戏IT新闻资讯~~~
严禁发布广告,淫秽、色情、赌博、暴力、凶杀、恐怖、间谍及其他违反国家法律法规的内容。!晓枫资讯-社区
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|晓枫资讯--科技资讯社区 本站已运行

CopyRight © 2022-2025 晓枫资讯--科技资讯社区 ( BBS.yzwlo.com ) . All Rights Reserved .

晓枫资讯--科技资讯社区

本站内容由用户自主分享和转载自互联网,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。

如有侵权、违反国家法律政策行为,请联系我们,我们会第一时间及时清除和处理! 举报反馈邮箱:点击这里给我发消息

Powered by Discuz! X3.5

快速回复 返回顶部 返回列表