news 2026/6/12 15:22:57

分布式锁 5 种实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
分布式锁 5 种实现

为什么需要分布式锁?

单机应用:synchronized / ReentrantLock ← JVM 内锁 分布式应用:多 JVM 实例,synchronized 不够用!← 需要分布式锁
MySQL 分布式锁最朴素
-- 用唯一索引实现分布式锁 CREATE TABLE distributed_lock ( lock_key VARCHAR(64) PRIMARY KEY, lock_value VARCHAR(64) NOT NULL, expire_time BIGINT NOT NULL ); -- 加锁:插入 INSERT INTO distributed_lock (lock_key, lock_value, expire_time) VALUES ('order_lock', 'uuid-123', UNIX_TIMESTAMP() + 30); -- 释放锁:删除 DELETE FROM distributed_lock WHERE lock_key = 'order_lock' AND lock_value = 'uuid-123';

Java 实现

public boolean tryLock(String lockKey, long expireSeconds) { String value = UUID.randomUUID().toString(); long expireTime = Instant.now().getEpochSecond() + expireSeconds; try { int rows = jdbcTemplate.update( "INSERT INTO distributed_lock (lock_key, lock_value, expire_time) VALUES (?, ?, ?)", lockKey, value, expireTime); return rows == 1; } catch (DuplicateKeyException e) { return false; // 锁已被其他线程持有 } } public boolean unlock(String lockKey, String value) { // ⚠️ 关键:必须验证 value(防止误删别人的锁) int rows = jdbcTemplate.update( "DELETE FROM distributed_lock WHERE lock_key = ? AND lock_value = ?", lockKey, value); return rows == 1; }

优缺点

  • ✅ 简单易用,有数据库就能用
  • 性能差(MySQL 写并发 1k+ TPS)
  • ❌ 锁等待靠轮询
  • ⚠️小流量场景可以用
Redis 分布式锁
1 SETNX + EX(最基础
// 加锁 public boolean tryLock(String lockKey, long expireSeconds) { String result = redisTemplate.opsForValue().set(lockKey, "locked", expireSeconds, TimeUnit.SECONDS); return "OK".equals(result); } // 释放锁 public void unlock(String lockKey) { redisTemplate.delete(lockKey); }

⚠️ 3 大问题

1.死锁:设置锁后宕机,锁永远不释放→ 用 EX 过期时间解决

2.误删锁:A 加锁 → A 业务慢 → 锁过期 → B 加锁 → A 释放锁(删了 B 的锁)→用 UUID 解决

3.不可重入:同一个线程不能再次获得锁 →用 ThreadLocal + 计数器解决

2 SETNX + UUID + Lua
public class RedisDistributedLock { // 加锁 Lua 脚本 private static final String LOCK_SCRIPT = "if redis.call('setnx', KEYS[1], ARGV[1]) == 1 then " + " redis.call('expire', KEYS[1], ARGV[2]) " + " return 1 " + "else " + " return 0 " + "end"; // 释放锁 Lua 脚本(⚠️ 必须验证 UUID,防止误删) private static final String UNLOCK_SCRIPT = "if redis.call('get', KEYS[1]) == ARGV[1] then " + " return redis.call('del', KEYS[1]) " + "else " + " return 0 " + "end"; public boolean tryLock(String lockKey, long expireSeconds) { String uuid = UUID.randomUUID().toString(); Long result = redisTemplate.execute( new DefaultRedisScript<>(LOCK_SCRIPT, Long.class), Arrays.asList(lockKey), uuid, String.valueOf(expireSeconds)); return result != null && result == 1; } public boolean unlock(String lockKey, String uuid) { Long result = redisTemplate.execute( new DefaultRedisScript<>(UNLOCK_SCRIPT, Long.class), Arrays.asList(lockKey), uuid); return result != null && result == 1; } }

实战代码

// DLQConsumer.java @RabbitListener(queues = "dlq.queue") public void processDLQ(ReportMessage message, Channel channel) throws IOException { String lockKey = "dlq:lock:" + message.getMessageId(); String uuid = UUID.randomUUID().toString(); try { // 1. Redis 分布式锁(30 秒过期) if (!redisLock.tryLock(lockKey, uuid, 30)) { log.info("任务已被其他 Consumer 锁定,跳过: {}", message.getMessageId()); channel.basicAck(message.getMessageProperties().getDeliveryTag(), false); return; } // 2. 业务处理 reportService.process(message); // 3. ACK channel.basicAck(message.getMessageProperties().getDeliveryTag(), false); } catch (Exception e) { log.error("处理失败", e); channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, false); } finally { // 4. 释放锁(验证 UUID) redisLock.unlock(lockKey, uuid); } }

优缺点

  • 性能高(Redis 写 10w+ TPS)
  • ✅ 实现简单
  • 不保证强一致(Redis 主从切换可能丢锁)
  • ❌ 需要自己实现可重入
Redisson 分布式锁
// 1. 引入 Redisson <dependency> <groupId>org.redisson</groupId> <artifactId>redisson-spring-boot-starter</artifactId> <version>3.23.4</version> </dependency> // 2. 配置 Redisson @Configuration public class RedissonConfig { @Bean public RedissonClient redissonClient() { Config config = new Config(); config.useSingleServer() .setAddress("redis://127.0.0.1:6379") .setPassword("mova_redis_pwd"); return Redisson.create(config); } } // 3. 使用分布式锁 @Service public class ReportTaskService { @Autowired private RedissonClient redissonClient; public void processReport(ReportTask task) { // ⚠️ 关键:可重入锁(基于 Redis Hash 实现) RLock lock = redissonClient.getLock("report:lock:" + task.getId()); try { // 1. 尝试加锁(10 秒过期,watchdog 自动续期) if (lock.tryLock(10, 30, TimeUnit.SECONDS)) { try { // 业务处理 reportService.process(task); } finally { // 2. 释放锁 lock.unlock(); } } else { log.info("获取锁失败,跳过任务: {}", task.getId()); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } }

Redisson 7 大优势

1.可重入:基于 Redis Hash,同一线程可多次加锁

2.看门狗自动续期:默认 30 秒续期一次,业务卡住锁不会过期

3.公平锁:按请求顺序排队

4.联锁(MultiLock):同时加多个锁(转账场景 A 减 + B 加

5.红锁(RedLock):5 个独立 Redis 节点(强一致

6.读写锁:RReadWriteLock

7.信号量:RSemaphore(限流场景

"项目用Redisson 分布式锁 + 看门狗自动续期机制30 秒自动续期,业务卡住锁不会过期4 个 JVM 实例同时抢锁只让 1 个实例处理任务任务零重复。"

Redisson 看门狗机制
┌────────────────────────────────────────────┐ │ Redisson 看门狗机制 │ ├────────────────────────────────────────────┤ │ 1. 加锁时设置 lockTime = 30 秒 │ │ ↓ │ │ 2. 后台线程每 10 秒检查锁是否还在 │ │ ↓ │ │ 3. 如果还在,自动续期 30 秒 │ │ ↓ │ │ 4. 业务执行完主动 unlock │ │ ↓ │ │ 5. 如果宕机,30 秒后锁自动过期 │ └────────────────────────────────────────────┘

"Redisson 看门狗机制:业务执行时锁自动续期,30 秒一次避免业务执行时间超过锁过期时间导致锁失效。"

Redisson 联锁(转账场景
// 转账场景:A 账户 -100,B 账户 +100,必须同时成功 public void transfer(String fromAccount, String toAccount, BigDecimal amount) { RLock lockA = redissonClient.getLock("lock:account:" + fromAccount); RLock lockB = redissonClient.getLock("lock:account:" + toAccount); // ⚠️ 联锁:要么都加成功,要么都失败 RedissonMultiLock multiLock = new RedissonMultiLock(lockA, lockB); try { if (multiLock.tryLock(10, 30, TimeUnit.SECONDS)) { try { // A 减 100 accountMapper.debit(fromAccount, amount); // B 加 100 accountMapper.credit(toAccount, amount); } finally { multiLock.unlock(); } } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }
Redisson 读写锁
// mpvs 客户数据:1 个写,多个读 public Order queryOrder(Long orderId) { RReadWriteLock rwLock = redissonClient.getReadWriteLock("order:lock:" + orderId); // 读锁(多个线程可同时读) RLock readLock = rwLock.readLock(); readLock.lock(); try { return orderMapper.selectById(orderId); } finally { readLock.unlock(); } } public void updateOrder(Order order) { RReadWriteLock rwLock = redissonClient.getReadWriteLock("order:lock:" + orderId); // 写锁(独占) RLock writeLock = rwLock.writeLock(); writeLock.lock(); try { orderMapper.updateById(order); } finally { writeLock.unlock(); } }

性能提升读多写少场景用读写锁,性能提升 5-10 倍

ZooKeeper 分布式锁
// ZK 分布式锁基于临时顺序节点 public class ZKDistributedLock { private ZooKeeper zk; private String lockPath = "/locks"; public void acquire(String lockKey) throws Exception { // 1. 创建临时顺序节点 String nodePath = zk.create(lockPath + "/" + lockKey + "_", null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL); // 2. 获取所有子节点 List<String> children = zk.getChildren(lockPath, false); Collections.sort(children); // 排序 // 3. 判断自己是否最小节点 if (nodePath.equals(lockPath + "/" + children.get(0))) { // 获取锁 } else { // 监听前一个节点 String prevNode = children.get(Collections.binarySearch(children, nodePath.substring(lockPath.length() + 1)) - 1); zk.exists(lockPath + "/" + prevNode, new Watcher() { @Override public void process(WatchedEvent event) { synchronized (this) { notifyAll(); // 唤醒等待 } } }); wait(); // 等待前一个节点释放 } } }

优缺点

  • 强一致(ZK 集群半数以上节点写成功才返回)
  • ✅ 公平锁(按请求顺序)
  • 性能差(ZK 集群写性能 1k+ TPS)
  • ❌ 部署复杂(需要 ZK 集群
  • ⚠️老项目用过
  • 新项目不推荐(用 Redisson 更简单)
RedLock 红锁强一致场景
// RedLock:5 个独立的 Redis 节点(部署在不同机器) public class RedLock { private List<RedissonClient> redissonClients; // 5 个独立的 Redis 节点 public boolean tryLock(String lockKey, long expireSeconds) { // 同时向 5 个节点请求加锁 List<RLock> locks = redissonClients.stream() .map(client -> client.getLock(lockKey)) .collect(Collectors.toList()); RedissonRedLock redLock = new RedissonRedLock(locks.toArray(new RLock[0])); try { // ⚠️ 关键:5 个节点中 > N/2 + 1 个加锁成功才算成功 return redLock.tryLock(100, expireSeconds * 1000, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { Thread.currentThread().interrupt(); return false; } } }

5 节点架构

┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ │ Redis1 │ │ Redis2 │ │ Redis3 │ │ Redis4 │ │ Redis5 │ └────────┘ └────────┘ └────────┘ └────────┘ └────────┘ ↑ ↑ ↑ ↑ ↑ └─────────┴─────────┴─────────┴─────────┘ RedissonRedLock (5 个节点中 >3 个加锁成功才算成功)

优缺点

  • 强一致(5 节点多数派,2 个节点同时挂掉也能用
  • 成本高(要部署 5 个独立 Redis)
  • 性能差(要等 5 个节点响应)
  • ⚠️金融项目强一致场景用(央行清算、跨行转账)
  • 一般业务用不上(Redisson 单 Redis 够用)

三、5 种分布式锁对比

维度MySQLRedis SETNXRedissonZooKeeperRedLock
性能1k TPS10w TPS10w+ TPS1k TPS5w TPS
可靠性中(主从切换可能丢锁)(看门狗)最高
强一致
可重入需自己实现
公平锁
复杂度极高
部署成本0(用现有 DB)0(用现有 Redis)高(要 ZK 集群)极高(5 Redis)
推荐度⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/12 15:22:36

为什么全球设备商都选 Metrix 国际物联网卡?

在智能硬件全球化量产出海的行业趋势下&#xff0c;设备厂商的核心竞争优势&#xff0c;早已不局限于终端产品本身的性能与品质&#xff0c;更取决于全球化部署效率、跨区域运维稳定性、长期合规风控能力与综合运营成本控制。多数出海设备故障、项目延期、售后纠纷&#xff0c;…

作者头像 李华
网站建设 2026/6/12 15:15:53

深入解析MCF5301x:高度集成SoC在VoIP与POS系统中的核心架构与实战设计

1. 项目概述与核心价值在嵌入式系统开发领域&#xff0c;选对一颗“心脏”——微处理器&#xff0c;往往决定了整个项目的成败。尤其是在那些对实时性、安全性和集成度要求都极高的应用场景里&#xff0c;比如我们日常接触的智能POS机、企业级IP电话或者网络语音网关&#xff0…

作者头像 李华
网站建设 2026/6/12 15:06:51

终极抢票神器DamaiHelper:10分钟轻松搞定演唱会门票

终极抢票神器DamaiHelper&#xff1a;10分钟轻松搞定演唱会门票 【免费下载链接】damaihelper 支持大麦网&#xff0c;淘票票、缤玩岛等多个平台&#xff0c;演唱会演出抢票脚本 项目地址: https://gitcode.com/gh_mirrors/dam/damaihelper 还在为抢不到心仪的演唱会门票…

作者头像 李华
网站建设 2026/6/12 15:05:51

Mermaid Live Editor:让图表创作从痛苦到愉悦的智能转换指南

Mermaid Live Editor&#xff1a;让图表创作从痛苦到愉悦的智能转换指南 【免费下载链接】mermaid-live-editor Edit, preview and share mermaid charts/diagrams. New implementation of the live editor. 项目地址: https://gitcode.com/GitHub_Trending/me/mermaid-live-…

作者头像 李华