实现分布式锁
在黑马点评项目中,在实现分布式锁的时候提到了实现的几种方式,本文来简单了解一下。
一、MySQL、Redis、ZooKeeper 是不是都是“数据库”?
严格来说,三者的定位和功能差异很大,但广义上都可以视为“数据存储系统”,不过它们的核心设计目标和适用场景完全不同。我们可以从“数据模型”和“核心用途”两个维度区分:
类型 | MySQL | Redis | ZooKeeper |
---|---|---|---|
核心定位 | 关系型数据库(OLTP,事务型存储) | 内存键值数据库(缓存/分布式工具) | 分布式协调系统(服务治理/元数据管理) |
数据模型 | 结构化数据(表、行、列,SQL支持) | 半结构化数据(键值对+丰富数据结构) | 树形结构(ZNode节点,小文件存储) |
存储方式 | 磁盘为主(内存缓存加速) | 内存为主(磁盘持久化可选) | 内存+磁盘(强一致性,少量写入) |
典型用途 | 业务数据的持久化存储(如订单、用户) | 缓存、会话、计数器、分布式锁等 | 服务注册发现、配置中心、分布式锁等 |
总结:MySQL是传统关系型数据库,Redis是高性能内存数据库,ZooKeeper是分布式协调工具(虽能存数据,但核心是解决分布式系统的一致性问题)。
二、三者都能实现分布式锁吗?MySQL的“排他锁”是什么?
是的,三者都能实现分布式锁,但实现方式和适用场景差异极大。我们逐个分析:
1. MySQL 实现分布式锁:基于表锁或行锁
MySQL的锁是数据库原生的排他锁(Exclusive Lock,X锁),通过SQL语句显式获取。
原理:利用数据库的行锁或表锁,在更新或查询时锁定资源,其他事务需等待锁释放后才能操作。
示例(行锁):
-- 开启事务
BEGIN;
-- 尝试获取锁(通过FOR UPDATE锁定某一行)
SELECT * FROM distributed_lock WHERE lock_name = 'order_lock' FOR UPDATE;
-- 如果未获取到锁(无结果),则回滚并重试;若获取到锁,执行业务逻辑
UPDATE distributed_lock SET owner = 'current_server' WHERE lock_name = 'order_lock';
COMMIT;
特点:
- 依赖数据库的事务和锁机制,强一致性(ACID)。
- 性能较差(每次加锁需数据库交互,网络延迟高)。
- 锁超时需手动处理(如设置
wait_timeout
,否则锁可能永久占用)。
2. Redis 实现分布式锁:基于原子命令或RedLock
Redis的分布式锁是其经典功能,核心依赖SETNX
(Set If Not Exists)命令的原子性。
原理:通过SET key value NX PX timeout
命令原子性地设置锁(仅当key不存在时设置,并自动过期)。
示例(单实例):
// Java中使用Redisson的RLock(底层即基于Redis的SETNX+过期时间)
RLock lock = redisson.getLock("order_lock");
boolean isLocked = lock.tryLock(10, 30, TimeUnit.SECONDS); // 等待10秒,锁自动30秒过期
if (isLocked) {try {// 执行业务逻辑} finally {lock.unlock();}
}
优化版本(RedLock):为避免单实例Redis故障导致锁失效,Redis作者提出RedLock算法(通过多个独立Redis实例投票获取锁)。
特点:
- 性能高(内存操作,网络延迟低)。
- 需处理锁续期(Redisson的“看门狗”自动续期)。
- 弱一致性(依赖Redis持久化配置,极端情况下可能丢锁)。
3. ZooKeeper 实现分布式锁:基于临时顺序节点
ZooKeeper的分布式锁基于其树形节点结构和Watcher机制,核心是利用“临时顺序节点”的特性。
原理:
- 所有客户端在锁路径(如
/lock
)下创建临时顺序节点(如/lock/node-000001
)。 - 客户端检查自己创建的节点是否是当前最小的节点:
- 若是,获取锁;
- 若否,监听前一个节点的删除事件(Watcher),等待前一个节点释放锁。
- 锁释放时(客户端主动删除节点或会话超时自动删除),触发后续节点的Watcher,唤醒下一个客户端获取锁。
特点:
- 强一致性(ZooKeeper保证分布式一致性)。
- 自动失效(客户端会话断开,临时节点自动删除,避免死锁)。
- 公平锁(按节点顺序分配锁)。
三、三者的核心区别总结
维度 | MySQL | Redis | ZooKeeper |
---|---|---|---|
核心目标 | 持久化存储结构化业务数据 | 高性能内存存储+分布式工具 | 分布式系统协调(一致性、元数据管理) |
数据存储 | 磁盘为主(事务优先) | 内存为主(持久化可选) | 内存+磁盘(强一致性,少量写入) |
分布式锁特性 | 强一致但性能差,需手动处理超时 | 高性能但需处理续期,弱一致性 | 强一致、自动失效、公平锁 |
典型场景 | 订单、用户等核心业务数据存储 | 缓存、会话、计数器、异步队列 | 服务注册发现、配置中心、分布式锁 |
四、ZooKeeper 是什么?
ZooKeeper 是 Apache 旗下的分布式协调服务中间件,专为解决分布式系统中的一致性问题设计。它的核心能力是提供可靠的分布式协调机制,让分布式应用可以高效协作。
ZooKeeper 的核心特性:
- 树形数据模型:数据以ZNode节点存储,类似文件系统的目录树(每个节点可存少量元数据)。
- 强一致性:通过ZAB协议(ZooKeeper Atomic Broadcast)保证分布式环境下数据的最终一致性。
- Watcher机制:客户端可监听节点变化(如创建、删除、数据修改),实现事件通知。
- 临时节点与顺序节点:临时节点随会话断开自动删除;顺序节点自动生成全局唯一序号。
ZooKeeper 的典型用途:
- 服务注册与发现:微服务中记录服务实例的地址(如Dubbo、Kafka)。
- 配置中心:集中管理分布式系统的配置(修改后实时推送至所有客户端)。
- 分布式锁:通过临时顺序节点实现公平锁(如前面的锁机制)。
- 集群管理:监控集群节点存活状态(通过心跳检测临时节点是否存在)。
总结
- MySQL是“业务数据的仓库”,核心是持久化存储和事务;
- Redis是“高性能工具箱”,核心是内存存储和分布式工具(如锁、队列);
- ZooKeeper是“分布式协调员”,核心是解决分布式系统的一致性和协作问题。
三者定位完全不同,但在分布式系统中常配合使用(例如:用MySQL存业务数据,Redis做缓存,ZooKeeper做服务发现)。