多个服务抢同一行记录,谁插入成功谁拿锁;插入失败说明锁被占,再看它是否过期,过期就可以抢过来。
1. 建锁表
CREATE TABLE distributed_lock (
lock_key VARCHAR(64) PRIMARY KEY,
owner VARCHAR(64) NOT NULL,
expire_at DATETIME NOT NULL
);
-
lock_key:锁的名字,比如order:123 -
owner:谁持有锁,比如 UUID -
expire_at:过期时间,防止死锁
2. 加锁流程
先尝试插入:
INSERT IGNORE INTO distributed_lock(lock_key, owner, expire_at)
VALUES ('order:123', 'uuid-1', NOW() + INTERVAL 30 SECOND);
-
插入成功:拿到锁
-
插入失败:说明已经有这行记录,锁被占了
然后尝试抢过期锁:
UPDATE distributed_lock
SET owner = 'uuid-1', expire_at = NOW() + INTERVAL 30 SECOND
WHERE lock_key = 'order:123' AND expire_at < NOW();
-
更新成功:说明旧锁过期了,你抢到了
-
更新失败:说明锁还在有效期内,你没抢到
3. 释放锁
只删自己的锁,防止误删别人的:
DELETE FROM distributed_lock
WHERE lock_key = 'order:123' AND owner = 'uuid-1';
4. 续期
业务还没执行完,但锁快过期了:
UPDATE distributed_lock
SET expire_at = NOW() + INTERVAL 30 SECOND
WHERE lock_key = 'order:123' AND owner = 'uuid-1';
5. 关键点
-
lock_key必须是主键或唯一索引,这是互斥的核心。 -
必须有过期时间,否则服务挂了锁永远不释放。
-
释放和续期都要带
owner,不能删别人的锁。 -
必须读写主库,不能读从库,否则主从延迟会误判。
-
适合并发不高的场景,高并发还是 Redis / ZooKeeper 更合适。
总结:
插入成功 = 拿锁;插入失败 = 看是否过期;过期就 UPDATE 抢锁;释放用 DELETE 带 owner 校验。