Go语言中用redis.Client实现分布式锁时,必须使用原子命令SET key value EX seconds NX,禁用分步的SETNX+EXPIRE;释放锁需用Lua脚本校验value一致性,避免误删;连接池、超时控制与context管理直接影响高并发下锁的可靠性。

Go 安装后 go version 报错或找不到命令
常见原因是 PATH 没配对,尤其是 macOS 或 Linux 下用二进制包安装时,/usr/local/go/bin 没加进 shell 配置文件。Windows 用户容易漏掉环境变量里的 GOROOT(应设为 Go 安装目录,如 C:\Go)和 PATH(需含 %GOROOT%\bin)。
验证方式:终端执行 which go(macOS/Linux)或 where go(Windows),无输出即失败。别急着重装,先检查:
-
echo $PATH或echo %PATH%是否含 Go 的 bin 路径 -
go env GOROOT是否指向正确目录(若为空,手动go env -w GOROOT=...) - 新开终端窗口——旧会话不读新配置
用 redis.Client 实现分布式锁时,SET key value EX 30 NX 不生效
这是最常被忽略的细节:原生命令必须用 redis.Client.Do 或封装好的原子操作,不能拆成 SETNX + EXPIRE。后者存在竞态——进程 A 设置成功,但还没来得及设过期就崩溃,锁永远不释放。
推荐直接用 github.com/go-redis/redis/v9 的 SetNX 方法,它底层自动拼 SET key value EX seconds NX:
立即学习“go语言免费学习笔记(深入)”;
ctx := context.Background()
ok, err := rdb.SetNX(ctx, "lock:order:123", "proc-789", 30*time.Second).Result()
if err != nil {
// 处理网络错误
}
if !ok {
// 锁已被占用
}
注意:value 必须全局唯一(比如用 UUID),否则释放锁时无法校验所有权,导致误删别人锁。
释放锁时用 DEL 导致“误删锁”
直接 rdb.Del(ctx, "lock:order:123") 是危险操作——你拿到的锁可能已被别人续期或覆盖,DEL 会删掉当前真正持有者的锁。
安全释放必须满足“只有自己才能删自己的锁”,得用 Lua 脚本保证原子性:
script := redis.NewScript(`
if redis.call("get", KEYS[1]) == ARGV[1] then
return redis.call("del", KEYS[1])
else
return 0
end
`)
result, err := script.Eval(ctx, rdb, []string{"lock:order:123"}, "proc-789").Result()
要点:
- 脚本里用
ARGV[1]对比 value,不是硬编码 - 返回值是
int64,1 表示删除成功,0 表示非本人锁 - 别用
EvalSha省事——首次调用必须Eval,否则 Redis 不认识脚本
本地跑通了,但压测时锁频繁失效或超时
这不是代码问题,而是 Redis 连接和上下文控制不当。常见原因:
- 没设置
context.WithTimeout,网络卡顿时阻塞整个 goroutine - Redis 连接池太小(默认 10),高并发下获取连接超时,表现为锁获取失败
- 没启用
redis.WithMinIdleConns(5),空闲连接太少,突发请求要重新建连
初始化 client 时至少加这三行:
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
PoolSize: 50, // 根据 QPS 调整,一般 20–100
MinIdleConns: 10,
})
ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
defer cancel()
分布式锁的可靠性不只取决于算法,更取决于你对 Redis 客户端行为的理解——连接、超时、重试、资源回收,每一步都可能成为单点故障。


















