在Go语言中实现Redis分布式锁主要依赖于Redis的SETNX命令和Lua脚本,确保在高并发环境下的原子性和可靠性4。
package main
import (
"context"
"fmt"
"log"
"time"
"github.com/go-redis/redis/v8"
"github.com/google/uuid"
)
var (
rdb *redis.Client
ctx = context.Background()
)
// 初始化Redis连接
func initRedis() {
rdb = redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // 无密码
DB: 0, // 默认数据库
})
}
// 获取分布式锁
func acquireLock(lockKey string, ttl time.Duration) (string, error) {
clientID := uuid.NewString()
result, err := rdb.SetNX(ctx, lockKey, clientID, ttl).Result()
if err != nil {
return "", err
}
if !result {
return "", fmt.Errorf("获取锁失败")
}
return clientID, nil
}
// 释放分布式锁的Lua脚本
const unlockScript = `
if redis.call("get", KEYS[1]) == ARGV[1] then
return redis.call("del", KEYS[1])
else
return 0
end
`
// 释放分布式锁
func releaseLock(lockKey, clientID string) error {
script := redis.NewScript(unlockScript)
result, err := script.Run(ctx, rdb, []string{lockKey}, clientID).Result()
if err != nil {
return err
}
if result.(int64) == 0 {
return fmt.Errorf("释放锁失败:不是锁的持有者")
}
return nil
}
// 自动续期
func renewLock(lockKey, clientID string, ttl time.Duration) error {
script := `
if redis.call("get", KEYS[1]) == ARGV[1] then
return redis.call("expire", KEYS[1], ARGV[2])
else
return 0
end
`
result, err := redis.NewScript(script).Run(ctx, rdb, []string{lockKey}, clientID, int(ttl/time.Second)).Result()
if err != nil {
return err
}
if result.(int64) == 0 {
return fmt.Errorf("续期失败:不是锁的持有者")
}
return nil
}
// 带重试的获取锁
func acquireLockWithRetry(lockKey string, ttl time.Duration, maxRetries int) (string, error) {
for i := 0; i < maxRetries; i++ {
clientID, err := acquireLock(lockKey, ttl)
if err == nil {
return clientID, nil
}
time.Sleep(100 * time.Millisecond)
}
return "", fmt.Errorf("获取锁超时")
}
func main() {
// 初始化Redis连接
initRedis()
// 测试锁功能
lockKey := "my_distributed_lock"
ttl := 10 * time.Second
// 获取锁
clientID, err := acquireLockWithRetry(lockKey, ttl, 5)
if err != nil {
log.Fatal("获取锁失败:", err)
}
fmt.Printf("成功获取锁,客户端ID: %s\n", clientID)
// 模拟业务处理
fmt.Println("执行关键业务操作...")
time.Sleep(5 * time.Second)
// 续期锁
err = renewLock(lockKey, clientID, ttl)
if err != nil {
log.Println("续期失败:", err)
} else {
fmt.Println("锁续期成功")
}
// 释放锁
err = releaseLock(lockKey, clientID)
if err != nil {
log.Fatal("释放锁失败:", err)
}
fmt.Println("锁释放成功")
}
该实现包含以下核心功能:
1. **原子性加锁**:使用SETNX命令确保只有一个客户端能成功获取锁。
2. **唯一标识**:通过UUID生成客户端唯一ID,避免误删其他客户端的锁。
3. **安全释放**:采用Lua脚本保证只有锁的持有者才能释放锁,操作具有原子性。
4. **自动续期**:提供锁的续期功能,防止业务执行时间过长导致锁自动过期。
5. **重试机制**:支持在获取锁失败时进行有限次数的重试。
6. **超时控制**:通过TTL设置锁的自动过期时间,避免死锁情况发生。
在实际使用中,建议将锁的TTL设置为业务处理时间的2-3倍,并定期检查锁的状态进行续期,确保分布式系统的数据一致性