返回
在 Go 中使用 Redis 实现轻量级、高性能的分布式锁
后端
2023-10-13 20:24:24
在分布式系统中,协调对共享资源的并发访问至关重要。分布式锁提供了一种机制,用于确保只有单个进程或线程在任何给定时间访问资源。本文将探讨如何使用 Go 和 Redis 来实现轻量级、高性能的分布式锁。
基于 Redis 的分布式锁
Redis 是一种流行的键值存储,提供原子的 SETNX(设置不存在)操作,可用于实现分布式锁。当需要获取锁时,客户端可以执行以下步骤:
- 使用 SETNX 为锁键设置一个值,该值通常是一个唯一标识符。
- 如果 SETNX 成功,则客户端已获取锁。
- 为锁键设置一个过期时间,以防止死锁。
Go 中的 Redis 分布式锁
以下 Go 代码演示了如何使用 Redis EXPIRE 命令实现分布式锁:
import (
"context"
"fmt"
"time"
"github.com/go-redis/redis/v8"
)
// getLock 获取分布式锁
func getLock(ctx context.Context, client *redis.Client, key string) (bool, error) {
// 设置锁的过期时间为 10 秒
expiration := time.Second * 10
// 使用 SETNX 原子地设置锁
ok, err := client.SetNX(ctx, key, "locked", expiration).Result()
if err != nil {
return false, fmt.Errorf("error setting lock: %w", err)
}
// 如果 SETNX 成功,则获取锁
return ok, nil
}
// releaseLock 释放分布式锁
func releaseLock(ctx context.Context, client *redis.Client, key string) error {
// 使用 DEL 原子地释放锁
err := client.Del(ctx, key).Err()
if err != nil {
return fmt.Errorf("error releasing lock: %w", err)
}
return nil
}
使用 Redis Lua 脚本改进
Redis Lua 脚本提供了比 SETNX 更高级别的原子操作。以下脚本可以原子地获取和释放锁:
-- 获取锁
local success = redis.call("setnx", KEYS[1], ARGV[1])
if success == 1 then
redis.call("expire", KEYS[1], ARGV[2])
end
return success
-- 释放锁
local value = redis.call("get", KEYS[1])
if value == ARGV[1] then
return redis.call("del", KEYS[1])
end
return 0
使用 Redis WATCH 增强
Redis WATCH 命令可以防止在执行 Lua 脚本时锁被其他客户端修改。以下 Go 代码演示了如何使用 WATCH:
import (
"context"
"fmt"
"time"
"github.com/go-redis/redis/v8"
)
// getLockWithWatch 获取分布式锁,使用 Redis WATCH 增强
func getLockWithWatch(ctx context.Context, client *redis.Client, key string) (bool, error) {
// 设置锁的过期时间为 10 秒
expiration := time.Second * 10
// 监控锁键的变化
if _, err := client.Watch(ctx, key).Result(); err != nil {
return false, fmt.Errorf("error watching key: %w", err)
}
// 使用 Lua 脚本原子地获取锁
ok, err := client.EvalSha(ctx, "getLockLuaScript", []string{key}, []string{"locked", expiration.String()}).Int64()
if err != nil {
return false, fmt.Errorf("error running Lua script: %w", err)
}
// 如果获取锁失败,则说明锁在 WATCH 期间已被修改
if ok == 0 {
return false, nil
}
return true, nil
}
结论
本文介绍了如何使用 Go 和 Redis 实现轻量级、高性能的分布式锁。通过结合 Redis EXPIRE、Lua 脚本和 WATCH,我们可以创建可靠且可扩展的分布式锁机制,以确保在分布式系统中对共享资源的并发访问。