谈一谈分布式锁实现方式

知其然要知其所以然,探索每一个知识点背后的意义,你知道的越多,你不知道的越多,一起学习,一起进步,如果文章感觉对您有用的话,关注、收藏、点赞,有困惑的地方请评论,我们一起交流!


分布式锁用于解决分布式系统中多个节点对共享资源的互斥访问问题,常见的实现方式主要包括以下几类,每种方式各有其适用场景和优缺点:

一、基于数据库的实现方式

1. 唯一索引锁(悲观锁)

  • 实现原理:在数据库表中创建唯一索引字段(如lock_key),通过插入一条唯一索引记录来获取锁(INSERT INTO locks(lock_key, ...) VALUES('resource', ...) ON DUPLICATE KEY UPDATE ...),释放锁时删除该记录。
  • 优点:简单直接,适用于低频场景。
  • 缺点
  • 依赖数据库可用性,若数据库故障则锁失效;
  • 锁的释放需显式操作,若节点崩溃未删除记录,可能导致死锁(需配合定时任务清理失效锁)。

2. 乐观锁(版本号 / 时间戳)

  • 实现原理:通过在资源表中增加version字段或timestamp,更新时检查版本号是否一致(如UPDATE resource SET value=? WHERE version=?),利用数据库的事务特性实现无锁竞争。
  • 优点:无锁等待,适合读多写少场景。
  • 缺点
  • 仅保证最终一致性,可能出现更新失败(需重试);
  • 不支持锁超时和可重入,功能有限。

二、基于缓存系统的实现方式

1. Redis 分布式锁(SET NX + 过期时间)

  • 核心命令
  • SET key value NX PX 过期时间:原子性获取锁(NX表示仅当键不存在时设置,PX设置过期时间防止死锁)。
  • 释放锁时需用Lua脚本保证原子性(避免误删其他节点的锁):
vbnet 复制代码
if redis.call('GET', KEYS[1]) == ARGV[1] then  
    return redis.call('DEL', KEYS[1])  
end  
  • 扩展:Redisson 框架提供可重入锁、公平锁、锁续约(自动延长过期时间)、红锁(Redlock)等功能,解决单点 Redis 故障问题(但 Redlock 的安全性存在争议,需谨慎使用)。
  • 优点:性能高,适合高频轻量锁场景。
  • 缺点
  • 主从同步延迟可能导致锁失效(如主节点未同步锁数据就宕机,从节点升级后锁丢失);
  • 不保证强一致性(Redis 基于 AP 模型)。

2. Memcached 分布式锁(CAS 机制)

  • 实现原理:通过CAS(Compare-And-Swap)命令尝试修改锁状态,类似乐观锁逻辑。
  • 缺点:功能简单,需自行处理超时和重试,使用场景较少。

三、基于分布式协调服务的实现方式

1. ZooKeeper 分布式锁

  • 核心原理
  • 利用 ZooKeeper 的临时顺序节点(Ephemeral Sequential Nodes):
  1. 每个节点尝试创建以/lock/为前缀的顺序子节点(如/lock/1000);
  1. 最小序号的节点获得锁,其他节点监听比自己小的前一个节点的删除事件,当前驱节点释放锁时重新竞争。
  • 优点
  • 基于 ZooKeeper 的强一致性(CP 模型),天然支持锁的公平性、可重入和自动释放(节点宕机后临时节点自动删除);
  • 社区成熟(如 Curator 框架封装了通用实现)。
  • 缺点
  • 实现较复杂,依赖 ZooKeeper 集群;
  • 频繁创建 / 删除节点可能产生性能开销。

2. etcd/Chubby 分布式锁

  • 原理类似 ZooKeeper
  • etcd 基于 Raft 协议,支持键的租约(Lease)和 Watch 机制,适合高可靠场景(如 Kubernetes 的租约锁);
  • Chubby 是 Google 的分布式锁服务,是 ZooKeeper 的灵感来源,工业级实现但不开源。

四、基于本地锁的扩展(分布式可重入锁)

1. Redisson 可重入锁(推荐)

  • 实现:基于 Redis 的SET NX和 Lua 脚本,支持可重入(通过记录持有锁的客户端 ID 和重入次数)、锁超时自动释放、跨节点锁续约等功能,接近 Java 本地锁(ReentrantLock)的体验。
  • 适用场景:需要复杂锁逻辑(如公平锁、读写锁)的分布式场景。

2. 数据库行锁(特殊场景)

  • 原理:通过数据库的行锁(如 MySQL 的SELECT ... FOR UPDATE)锁定特定资源的记录,利用数据库事务保证互斥。
  • 缺点:锁粒度粗,性能差,仅适用于与数据库强绑定的业务场景。

五、各实现方式对比与选择建议

实现方式 核心特性 优点 缺点 适用场景
数据库唯一索引锁 简单、依赖数据库 实现简单 性能低、易死锁 低频、简单业务
Redis 分布式锁 高性能、轻量 响应快、支持高并发 主从同步问题、弱一致性 高频、非强一致场景
Redisson 框架 功能丰富(可重入、续约) 接近本地锁体验、支持复杂锁逻辑 Redlock 安全性争议 通用分布式锁场景
ZooKeeper 锁 强一致性、公平性 可靠、支持自动容错 实现复杂、性能开销 强一致、高可靠场景
etcd 分布式锁 高可用性、分布式协调 适合分布式系统元数据锁 依赖 etcd 集群 微服务协调、分布式配置中心

六、分布式锁的核心设计原则

  1. 互斥性:同一时刻仅一个节点持有锁;
  1. 容错性:节点或服务宕机时,锁能自动释放(如设置合理的过期时间);
  1. 可重入性:同一节点可多次获取同一把锁而不阻塞;
  1. 锁超时:避免死锁,需结合业务逻辑设置合理的锁有效期;
  1. 公平性(可选):保证锁获取的顺序性(如 ZooKeeper 的顺序节点)。

总结

选择分布式锁实现时,需结合业务场景的一致性要求(AP/CP)、性能需求和复杂度:

  • 高频轻量场景:优先 Redis + Redisson(兼顾性能与功能);
  • 强一致高可靠场景:ZooKeeper 或 etcd(适合分布式协调、元数据管理);
  • 简单低频场景:数据库唯一索引或乐观锁(避免引入额外中间件)。无论哪种方式,都需注意锁的原子性操作、过期时间设计和异常处理,避免出现 "分布式竞态条件"。
相关推荐
方圆想当图灵4 分钟前
由 Mybatis 源码畅谈软件设计(七):SQL “染色” 拦截器实战
后端·mybatis·代码规范
向哆哆25 分钟前
Java 安全:如何防止 DDoS 攻击?
java·安全·ddos
啥都想学的又啥都不会的研究生29 分钟前
Kubernetes in action-初相识
java·docker·微服务·容器·kubernetes·etcd·kubelet
毅航29 分钟前
MyBatis 事务管理:一文掌握Mybatis事务管理核心逻辑
java·后端·mybatis
我的golang之路果然有问题44 分钟前
速成GO访问sql,个人笔记
经验分享·笔记·后端·sql·golang·go·database
柏油1 小时前
MySql InnoDB 事务实现之 undo log 日志
数据库·后端·mysql
宝耶1 小时前
面试常问问题:Java基础篇
java·面试·职场和发展
躲在云朵里`1 小时前
IDEA搭建环境的五种方式
java·ide·intellij-idea
喵手2 小时前
从 Java 到 Kotlin:在现有项目中迁移的最佳实践!
java·python·kotlin
阑梦清川2 小时前
AI超级智能体项目教程(二)---后端项目初始化(设计knif4j接口文档的使用)
java·前端·数据库