1. 分布式锁到底解决什么问题
在开始动手之前,有必要先把分布式锁这件事的来龙去脉说清楚。很多人一听到“分布式锁”就觉得是个高大上的中间件,或者以为买个组件装上就能一劳永逸,其实不是这么回事。分布式锁本质上是多个进程之间互斥访问共享资源的一种同步机制,它解决的是分布式系统里“多个节点同时操作同一份数据”的竞争问题。
1.1 单机锁为什么在分布式场景下失效
从单体应用时代过来的开发者,对锁的理解一般停留在 synchronized 或者 ReentrantLock 上。这类锁的核心逻辑是:在同一个JVM进程内,多个线程竞争同一个锁对象,谁拿到锁谁就执行,执行完释放锁。单个进程内,JVM能保证所有线程都在自己的内存模型里看到一致的锁状态,所以一切正常。
但到了分布式环境下,业务部署变成了多台机器,用户请求可能分别落在不同的服务器上。假设有两台机器同时收到了“给用户A扣减100元余额”的请求,如果不加锁,两台机器上的代码都会先读余额,再扣减,最后写回,在并发情况下就很容易出现数据不一致。这时候单机锁完全帮不上忙,因为机器1上的锁和机器2上的锁根本就是两个对象,互相感知不到对方的存在。
一句话总结:单机锁是线程级别的,分布式锁是进程级别的,两者解决的问题域不同,不能混为一谈。
1.2 什么样的业务场景必须用分布式锁
实践中常见的场景有三类。第一类是秒杀和库存扣减,这类业务对一致性要求极高,不允许多个请求同时把最后一件商品的库存扣成负数。第二类是分布式任务调度,比如定时任务在多台机器上部署,必须保证同一时刻只有一个节点在跑某个任务,否则数据会被重复处理,消息会被重复发送。第三类是分布式事务中的幂等控制,比如订单支付的回调、MQ消息的消费,同一个事件可能被触发多次,需要通过锁来保证只有一次有效处理。
判断一个场景是否需要分布式锁,可以从两个维度来思考:并发冲突程度有多高,以及冲突后造成的损失有多大。如果一个接口每秒几百上千的并发量,操作的是同一个热点ID,同时冲突后的影响是用户资金或订单状态错乱,那基本上就必须上分布式锁了。
1.3 分布式锁的三个核心特性
一个合格的分布式锁,我认为至少要满足以下三点:
- 互斥性:任意时刻,只能有一个客户端持有锁。
- 死锁防护:持有锁的客户端宕机或网络异常时,锁必须能被自动释放,不能一直卡死其他请求。
- 可重入性(视场景而定):同一个客户端在持有锁的情况下再次获取同一把锁,应该能够成功,避免业务内嵌套调用时把自己锁死。
除此之外,在生产环境里还得考虑锁的性能开销、实现成本和运维成本。分布式锁不是越复杂越好,适合当前业务形态、能稳定运行、出问题时好排查,这才是最重要的。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 主流实现方案对比与选型思路
从工程实践来看,分布式锁的主流落地方案大致有三种:基于关系型数据库、基于Redis、基于ZooKeeper(以及类似ZooKeeper的Etcd)。每种方案都有自己适合的场景,也都有自己的坑。
2.1 基于数据库的分布式锁
用数据库实现分布式锁,思路非常朴素:在数据库里建一张锁表,锁的key做唯一索引,获取锁就是往表里插入一条记录,释放锁就是删除这条记录。
sql复制CREATE TABLE `distributed_lock` (
`lock_key` varchar(64) NOT NULL COMMENT '锁的key',
`owner` varchar(64) NOT NULL COMMENT '持有者标识',
`expire_time` datetime DEFAULT NULL COMMENT '过期时间',
PRIMARY KEY (`lock_key`)
) ENGINE=InnoDB;
获取锁时执行 `INSERT INTO distributed_lock(lock_key
