2026/5/21 9:34:02
网站建设
项目流程
北京网站建设厂家,免费网站建设有哪些,建筑行业综合查询平台,大型图片库网站建设视频看了几百小时还迷糊#xff1f;关注我#xff0c;几分钟让你秒懂#xff01; 在分布式系统中#xff0c;多个服务实例同时操作共享资源#xff08;如扣减库存、生成订单号#xff09;时#xff0c;必须使用分布式锁来保证数据一致性。而 Redis 凭借其高性能和原子操…视频看了几百小时还迷糊关注我几分钟让你秒懂在分布式系统中多个服务实例同时操作共享资源如扣减库存、生成订单号时必须使用分布式锁来保证数据一致性。而 Redis 凭借其高性能和原子操作成为实现分布式锁的首选方案。但看似简单的SETNX背后却隐藏着无数陷阱锁失效、死锁、主从切换丢锁、误删他人锁……稍有不慎轻则数据错乱重则资损事故本文将带你✅ 深入理解 Redis 分布式锁的核心原理✅ 用Java Spring Boot实现高可用、可重入、防误删的分布式锁✅ 揭露 4 大经典反例与生产级避坑指南一、为什么需要分布式锁 场景电商秒杀扣库存// 单机环境下synchronized 足够 public void deductStock(Long productId) { synchronized (this) { int stock getStockFromDB(productId); if (stock 0) { updateStock(productId, stock - 1); // 扣减 } } }❌问题当部署多个服务实例如 3 台服务器synchronized只在 JVM 内生效无法跨进程同步✅ 解决方案Redis 分布式锁所有实例竞争同一把“Redis 锁”谁拿到锁谁操作数据库保证同一时间只有一个实例执行关键代码二、基础版SETNX EXPIRE❌ 有致命缺陷❌ 反例 1非原子操作 → 死锁风险// 错误写法 Boolean locked redisTemplate.opsForValue().setIfAbsent(lock:order, 1); if (locked) { // 设置过期时间防止业务卡死导致锁永不过期 redisTemplate.expire(lock:order, 10, TimeUnit.SECONDS); try { // 执行业务... } finally { redisTemplate.delete(lock:order); // 释放锁 } } 问题分析setIfAbsent()和expire()是两个命令非原子如果在setIfAbsent成功后、expire执行前服务宕机 →锁永不过期 → 死锁三、正确姿势 1SET 命令原子加锁Redis 2.6.12Redis 的SET命令支持NX不存在才设 EX/PX自动过期组合原子性保证。✅ Spring Boot 实现基础版public boolean tryLock(String lockKey, String requestId, long expireTimeMs) { Boolean result redisTemplate.execute( (RedisCallbackBoolean) connection - connection.set( lockKey.getBytes(), requestId.getBytes(), Expiration.milliseconds(expireTimeMs), RedisStringCommands.SetOption.SET_IF_ABSENT ) ); return Boolean.TRUE.equals(result); } public void unlock(String lockKey, String requestId) { // 注意这里仍有问题见下文 redisTemplate.delete(lockKey); }关键点requestId建议用UUID 线程ID用于标识锁持有者expireTimeMs必须设置防止死锁❌ 但解锁仍有问题可能误删他人锁假设服务 A 拿到锁但业务执行超时10s锁自动过期服务 B 拿到同一把锁此时服务 A 执行完调用delete(lockKey)→删掉了服务 B 的锁四、正确姿势 2Lua 脚本保证“判断删除”原子性要安全释放锁必须满足只有锁的持有者requestId 匹配才能删除锁这需要先 GET 判断值再 DEL但两步操作非原子 → 必须用Lua 脚本✅ 安全解锁 Lua 脚本-- unlock.lua if redis.call(GET, KEYS[1]) ARGV[1] then return redis.call(DEL, KEYS[1]) else return 0 end✅ Spring Boot 集成Component public class RedisDistributedLock { Autowired private StringRedisTemplate redisTemplate; private static final DefaultRedisScriptLong UNLOCK_SCRIPT; static { UNLOCK_SCRIPT new DefaultRedisScript(); UNLOCK_SCRIPT.setLocation(new ClassPathResource(lua/unlock.lua)); UNLOCK_SCRIPT.setResultType(Long.class); } public boolean tryLock(String lockKey, String requestId, long expireTimeMs) { Boolean result redisTemplate.opsForValue() .setIfAbsent(lockQey, requestId, Duration.ofMillis(expireTimeMs)); return Boolean.TRUE.equals(result); } public void unlock(String lockKey, String requestId) { redisTemplate.execute(UNLOCK_SCRIPT, Collections.singletonList(lockKey), requestId); } }✅优势Lua 脚本在 Redis 中原子执行杜绝误删五、进阶需求可重入锁 自动续期问题 1同一线程能否多次获取同一把锁可重入场景方法 A 加锁 → 调用方法 B也需要同一把锁基础版会阻塞 → 需要可重入锁问题 2业务执行时间 锁过期时间自动续期如锁 TTL30s但业务需 60s → 锁提前释放 → 并发安全失效✅ 解决方案Redisson生产推荐Redisson是 Redis 官方推荐的 Java 客户端内置可重入、自动续期、看门狗机制的分布式锁。Maven 引入dependency groupIdorg.redisson/groupId artifactIdredisson-spring-boot-starter/artifactId version3.23.5/version /dependencySpring Boot 使用Service public class OrderService { Autowired private RedissonClient redissonClient; public void createOrder() { RLock lock redissonClient.getLock(lock:order:create); try { // 尝试加锁最多等待 10 秒上锁后 30 秒自动解锁 boolean locked lock.tryLock(10, 30, TimeUnit.SECONDS); if (!locked) { throw new RuntimeException(获取锁失败); } // 执行业务即使超过 30 秒Redisson 会自动续期 doCreateOrder(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { lock.unlock(); // 可重入只有最外层 unlock 才真正释放 } } }Redisson 核心机制看门狗Watchdog只要线程持有锁每隔 10s 自动续期TTL 重置为 30s可重入计数同一线程多次加锁内部计数器 1unlock 时 -1归零才释放公平锁/读写锁支持更复杂场景六、四大经典陷阱与避坑指南陷阱后果解决方案非原子加锁死锁用SET key val NX EX 10原子命令无标识解锁误删他人锁用 Lua 脚本校验 requestId锁过期时间固定业务未完成锁已丢用 Redisson 自动续期主从架构丢锁主节点宕机从节点未同步锁 → 多个客户端同时持锁使用RedLock争议大或ZooKeeper / ETCD⚠️关于 RedLockRedis 作者提出的一种多节点容错方案但 Martin Kleppmann 等专家指出其在时钟漂移场景下仍不安全。一般业务建议用 Redisson 单 Redis 实例高可用由哨兵/Cluster 保证即可。七、总结分布式锁最佳实践永远不要用SETNX EXPIRE分开写解锁必须用 Lua 脚本校验持有者 ID业务时间不确定上 Redisson高可用靠 Redis 哨兵/Cluster而非 RedLock锁的粒度尽量小减少持有时间结语分布式锁看似简单实则暗流涌动。一个没有自动续期、没有持有者校验的锁等于没有锁在生产环境中强烈建议直接使用 Redisson它已经帮你踩平了所有坑。自己造轮子除非你愿意承担资损风险视频看了几百小时还迷糊关注我几分钟让你秒懂