外观
Redis 原子性与分布式锁
- 状态:未准备
- 适用方向:Go 全栈 / Go 后端
- 最近复习:待填写
- 下次复习:待安排
一句话回答
Pipeline 主要减少往返,事务或 Lua 提供特定原子执行边界;分布式锁还要处理租约到期、进程暂停、主从切换与旧持有者继续写入。
原理解释
Pipeline 批量发命令,不能保证其他客户端命令不插入。MULTI/EXEC 中命令按顺序连续执行,但没有关系型事务式回滚;WATCH 提供乐观条件检查。Lua 可原子地比较与修改,长脚本会阻塞其他操作;脚本后续报错也不会自动撤销已完成的写入。
基础锁用 SET key unique-token NX PX ttl 把占有条件与租约一条命令设置,避免 SETNX 后再 EXPIRE 的崩溃窗口。释放时比较 token 再删除,不能只 DEL:原锁可能过期并被其他持有者获得。
lua
if redis.call('GET', KEYS[1]) == ARGV[1] then
return redis.call('DEL', KEYS[1])
end
return 0脚本适用于 value 为所有权 token 的 String 锁;Hash 等结构需按实际数据模型改写。Cluster 多 key 原子脚本需满足同槽等约束。
场景与边界
续租失败或进程长暂停后,旧持有者可能仍继续工作。严格保护外部写入时,需要目标存储校验单调递增 fencing token 或采用数据库条件写等方案,唯一随机 token 只能识别所有者,不能排序新旧。异步复制的故障切换也可能破坏单实例锁假设。
项目中的应用
EinoTalk 路由清理用比较所有权后删除避免旧连接清理新映射;这是条件清理,不等于已经实现通用分布式锁。
常见追问
- 有过期就安全? 只减少永久占用,不保证业务在租约内完成。
- Lua 原子等于持久化? 不等于,执行隔离与故障恢复保证不同。
- 数据库唯一约束能替代锁吗? 创建唯一业务记录时往往更直接,但不能替代所有互斥需求。