Redis分布式锁首选StackExchange.Redis因其通过LockTake/LockRelease配合Lua脚本保障原子性,避免手动拼写SET命令导致的续期遗漏、误删等问题;锁key需带业务前缀、过期时间设为业务最大耗时2–3倍且≤30秒,务必在finally中用LockRelease传入lockToken防误删。

Redis实现分布式锁为什么首选StackExchange.Redis
因为官方客户端不支持原子性操作,而StackExchange.Redis能用LockTake + LockRelease配合Lua脚本保证加锁/解锁的原子性。直接用SET key value EX seconds NX虽然可行,但自己拼Lua容易漏掉过期时间续期、误删其他客户端锁等边界问题。
实操建议:
- 用
IDatabase.LockTake而非手写SET命令,它内部已封装Lua并校验value一致性 - 锁key必须带业务标识前缀,比如
"lock:order:create:{userId}",避免不同服务冲突 - 过期时间设为业务最大执行时间的2–3倍,但别超过Redis最大超时容忍(如30秒),否则可能被自动驱逐
- 务必在
finally块里调用LockRelease,且传入当初LockTake返回的lockToken——这是防误删的关键
数据库行级锁在SQL Server里怎么安全用
用SELECT ... WITH (UPDLOCK, ROWLOCK, HOLDLOCK)能拿到行锁,但只在事务内有效,且依赖唯一索引。没索引或条件不精确会导致锁升级成页锁甚至表锁,拖垮整个库。
实操建议:
- WHERE条件字段必须有唯一索引,例如
WHERE OrderNo = @orderNo,OrderNo是唯一键 - 必须显式开启事务,
using var tx = conn.BeginTransaction(),不能靠隐式事务 - 锁住的记录必须真实存在,否则
UPDLOCK会锁住间隙(gap lock),阻塞无关插入 - 不要在锁住行后做HTTP调用、文件读写等长耗时操作,否则锁持有时间不可控
Redis锁续期失败导致提前释放怎么办
常见现象是任务还没跑完,锁就过期了,另一个实例进来并发修改数据。根本原因是没做watchdog机制,或者用错续期方式——比如用新value覆盖旧token,导致LockRelease校验失败。
实操建议:
- 用
LockExtend方法续期,它会校验当前token是否仍属于本客户端 - 续期频率设为锁过期时间的1/3,比如锁10秒,每3秒续一次
- 如果用自定义方案,续期Lua必须先
GET再EXPIRE,且只在value匹配时才更新过期时间 - 注意:StackExchange.Redis 2.6.115+才支持
LockExtend,低版本需自行实现
MySQL上实现分布式锁要注意什么
MySQL没有原生分布式锁函数,GET_LOCK()是会话级的,跨连接无效;INSERT ... ON DUPLICATE KEY UPDATE倒是可用,但失败时得轮询或重试,吞吐差。
实操建议:
- 优先建一张
distributed_lock表,主键为lock_key,加唯一约束 - 加锁用
INSERT INTO distributed_lock (lock_key, owner_id, expire_at) VALUES (@key, @id, DATE_ADD(NOW(), INTERVAL 30 SECOND)) - 解锁必须用
DELETE FROM distributed_lock WHERE lock_key = @key AND owner_id = @id,防止删错 - 定期用后台任务清理
expire_at 的死锁记录,但清理逻辑本身要加锁,避免竞争


















