1. 分布式锁的本质与核心诉求
在分布式系统中,多个服务实例需要协调对共享资源的访问时,传统的单机线程锁机制(如synchronized或ReentrantLock)就完全失效了。想象一下这样的场景:三个订单服务实例同时尝试扣减同一商品的库存,如果没有跨进程的协调机制,必然会出现超卖问题。这正是分布式锁要解决的核心问题——在分布式环境下实现跨进程的互斥访问。
分布式锁需要满足三个铁律:
- 互斥性:在任何时刻,只能有一个客户端持有锁。这是锁最基础的功能,也是实现资源独占访问的前提。
- 防死锁:即使锁持有者崩溃,锁也必须在合理时间内自动释放。没有这个特性,系统就会因为偶发的进程崩溃而逐渐僵死。
- 高可用:锁服务本身必须具有高可用性,不能成为系统的单点故障。这意味着我们需要考虑锁服务的集群部署和故障转移。
关键认知误区:很多人以为只要用Redis的SETNX命令就是分布式锁了。实际上,一个生产可用的分布式锁需要考虑网络分区、时钟漂移、客户端阻塞等复杂场景,这些才是真正体现设计功力的地方。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 数据库分布式锁的实现与陷阱
2.1 基于SELECT FOR UPDATE的实现
最直观的实现方式是利用数据库的行锁机制。以MySQL为例,可以通过以下方式实现:
sql复制BEGIN;
SELECT * FROM distributed_lock WHERE lock_name='order_lock' FOR UPDATE;
-- 执行业务逻辑
COMMIT;
这种方式的优点是实现简单,直接利用现有数据库基础设施。但存在几个致命缺陷:
- 性能瓶颈:所有锁请求都需要走数据库事务,在高并发场景下会成为系统瓶颈。我们曾在一个秒杀系统中测试,QPS超过200后数据库连接就被耗尽。
- 死锁风险:如果应用崩溃忘记提交事务,会导致锁长时间不释放。虽然可以通过设置事务超时缓解,但这又引入了新的复杂度。
- 表锁风险:如果WHERE条件没有走索引,InnoDB会退化为表锁,导致完全失去并发能力。
2.2 基于唯一索引的实现
另一种变通方案是利用唯一索引冲突:
sql复制-- 建表语句
CREATE TABLE `distributed_lock` (
`lock_name` varchar(64) NOT NULL,
`owner` varchar(64) NOT NULL,
`expire_time` datetime NOT NULL,
PRIMARY KEY (`lock_name`),
UNIQUE KEY `idx_owner` (`owner`)
) ENGINE=InnoDB;
-- 获取锁
INSERT INTO distributed_lock(lock_name, owner, expire_time)
VALUES ('order_lock', 'host1_pr
