分布式锁详解-MySQL、ZooKeeper与Redis三种实现方案

发布时间:2026/8/13 17:01:18
分布式锁详解-MySQL、ZooKeeper与Redis三种实现方案 在分布式系统中多个进程/线程需要并发访问共享资源为保证资源访问的互斥性与一致性必须引入分布式锁进行控制。本文从锁的本质要求出发系统梳理分布式锁的三种主流实现方式及其优缺点。一、锁的本质要求无论什么锁都必须满足以下三个基本性质互斥性任何时刻只能有一个线程持有锁。可重入性一个线程已经持有锁后可以多次获取该锁而不发生死锁。安全性可释放一个线程获得锁后即使崩溃或失去连接锁也必须被释放不能永久占用。二、分布式锁的更高要求分布式环境对锁提出了更高的要求高性能锁可能被很多台服务器同时获取必须能够高性能地获取和释放。高可用不能因为某个提供锁的服务不可用导致所有服务都拿不到或释放不了锁因此要满足高可用。锁失效机制假设某应用获取锁后一直没有释放可能服务已挂不能一直占着锁否则其他服务永远获取不到。必须有自动失效机制。非阻塞特性某服务来获取锁时若锁已被其他服务持有应直接返回失败而不是无限等待。三、为什么需要分布式锁当多个进程或线程需要访问共享资源时为保证资源访问的互斥性需要借助锁进行控制。在单机环境下Java 的synchronized、ReentrantLock等本地锁即可胜任。但在分布式系统中由于数据存储与处理分散在多个节点上本地锁无法跨节点生效因此必须使用分布式锁来保证对共享资源访问的互斥性和一致性。常见的分布式锁实现方式有三类基于数据库MySQL基于缓存Redis基于协调服务ZooKeeper四、基于 MySQL 实现分布式锁4.1 实现方式基于数据库的实现方式通常利用数据库的事务特性来保证锁的正确性将锁状态存储在数据库中。常见有两种做法方式一利用行级锁使用数据库的行级锁保证对同一资源访问的互斥例如-- 通过 select ... for update 锁定目标行SELECT*FROMlock_tableWHEREresource_id?FORUPDATE;或通过UPDATE ... WHERE语句实现。方式二利用唯一索引使用数据库的唯一索引实现分布式锁例如-- resource_id 作为唯一索引-- 若记录不存在则插入成功(获得锁)已存在则更新(或其他处理)INSERTINTOlock_table(resource_id,owner,expire_time)VALUES(?,?,?)ONDUPLICATEKEYUPDATEownerVALUES(owner);原理将资源的唯一标识作为唯一索引。每次获取锁时尝试插入一条记录插入成功即获得锁若记录已存在则执行更新否则插入新记录。4.2 优点实现简单相比 ZooKeeper 方式基于 MySQL 的实现只需利用事务和唯一索引即可逻辑清晰、上手快。无需额外依赖MySQL 是常见数据库不需要引入额外的依赖库。对于尚未使用 ZooKeeper 的系统可以较为方便地引入基于 MySQL 的分布式锁。4.3 缺点对 MySQL 可用性和性能要求高与 ZooKeeper 方式一样需要保证 MySQL 集群的可用性和性能否则将影响整个系统的正常运行。不适合高频加锁/解锁基于事务机制实现锁管理在高频加解锁场景下会产生大量数据库连接和事务操作导致性能瓶颈。不适合长时间占用锁由于使用行级锁或表锁实现不适合长时间占用资源。五、基于 ZooKeeper 实现分布式锁5.1 实现方式基于 ZooKeeper 的实现方式通常利用 ZooKeeper 的节点特性实现核心是临时节点 Watch 机制创建临时节点在 ZooKeeper 上创建临时节点每个节点对应一把锁锁的持有者在创建节点时添加自己的标识释放锁时删除对应节点即可。Watch 等待机制利用 ZooKeeper 的 Watch 机制当节点被删除时通过 Watch 通知等待者从而实现锁的等待机制。5.2 优点实现简单ZooKeeper 本身已实现分布式协调服务提供了临时节点、Watch 机制等原语可方便地实现分布式锁。容错能力强ZooKeeper 是高可用的分布式协调服务能保证分布式锁在出现故障时的可用性。分布式锁管理效果好ZooKeeper 本身就是分布式服务在分布式环境下使用它能较好地解决锁管理问题。5.3 缺点对 ZooKeeper 可用性和性能要求高其可用性和性能直接决定分布式锁的表现集群一旦不稳定或出现性能瓶颈会影响整个系统的正常运行。依赖性强需要引入 ZooKeeper 依赖库对于未使用 ZooKeeper 的系统需额外增加依赖。六、基于 Redis 实现分布式锁6.1 实现方式基于缓存的实现方式通常使用分布式缓存来存储锁状态核心是 Redis 的SETNX命令使用SETNX获取锁SETNXSET if Not eXists命令在键不存在时设置该键的值键已存在则不做任何操作因此可用它来实现分布式锁# 键不存在则设置成功(获得锁)返回1键已存在则失败返回0SETNX lock_key owner_value使用带过期时间的SETNX通过为锁设置过期时间保证锁的自动过期避免死锁# 原子地设置值并指定过期时间(秒)SET lock_key owner_value NX PX300006.2 优点实现简单只需利用缓存的 CAS 原语SETNX和过期时间即可实现。支持高并发缓存系统一般支持高并发读写能较好地支撑高并发场景下的分布式锁。有效避免死锁过期时间机制可以较好地避免死锁——一旦某节点因故障未释放锁过期时间到达后锁会自动失效不影响系统整体正常运行。6.3 缺点依赖缓存系统的可用性和性能与 MySQL、ZooKeeper 方式一样依赖缓存系统的稳定与性能缓存不稳定或出现瓶颈会影响系统正常运行。可靠性相对较低缓存本质是内存存储系统相比 ZooKeeper 和 MySQL可靠性稍低。发生故障时缓存中的数据可能丢失因此需要设计合理的故障恢复机制。七、三种实现方式对比总结维度MySQLZooKeeperRedis核心机制事务 行锁/唯一索引临时节点 WatchSETNX 过期时间实现复杂度简单简单简单额外依赖无MySQL 常见需引入 ZooKeeper 依赖需引入 Redis 依赖性能低高频场景瓶颈明显中高支持高并发可靠性高高中内存存储需容灾防死锁依赖释放逻辑临时节点自动清理过期时间自动失效适用场景低频、中小规模对一致性要求高高并发、性能敏感选型建议追求高并发、高性能→ 优先选Redis注意做好锁过期续期如 Redisson 看门狗与容灾设计。强一致性、强可靠性、需容错→ 优先选ZooKeeper。已有 MySQL、低频场景、不想引入新依赖→ 可选MySQL但避免高频加解锁。八、总结分布式锁是解决分布式环境下共享资源互斥访问的必备手段。三种主流实现各有取舍MySQL实现简单、零额外依赖但性能受限、不适合高频。ZooKeeper利用临时节点 Watch容错强、一致性高但依赖性强。Redis基于SETNX 过期时间性能高、能自动防死锁但可靠性稍低、需容灾。实际生产应结合性能、一致性、可靠性、现有技术栈综合选型并为锁配备自动失效与故障恢复机制才能满足分布式环境下高性能、高可用、可失效、非阻塞的要求。

相关新闻