使用Redlock算法实现分布式锁,以解决单Redis节点的单点故障问题,确保分布式环境下的锁安全性。
Redlock是Redis官方推荐的分布式锁实现方案,它基于多个独立的Redis节点(通常是3个或5个),通过在多数节点上获取锁来保证分布式锁的可靠性,即使部分节点故障,锁依然有效。
Redlock的核心流程分为获取锁和释放锁两步:
获取锁: 生成唯一的随机值(如UUID)作为锁的标识,用于防止误删其他客户端的锁。依次向所有Redis节点发送
SET key random_value NX PX expire_time命令(NX=不存在则设置,PX=毫秒过期)。计算成功获取锁的节点数,若超过半数(如3节点需≥2,5节点需≥3)且总耗时≤锁过期时间,则认为锁获取成功;否则,在所有节点上释放锁(避免死锁)。
释放锁:
向所有Redis节点发送Lua脚本,验证随机值匹配后删除锁(防止误删其他客户端的锁)。
适用于分布式系统(如微服务、多实例应用)中需要强一致性锁的场景,例如:
分布式任务调度(避免重复执行)秒杀/库存扣减(防止超卖)共享资源访问控制(如数据库分片写操作)使用成熟的Redlock实现库
redsync(Redis官方推荐):
go get github.com/go-redsync/redsync/v4
go get github.com/go-redsync/redsync/v4/redis/goredis/v9
package main
import (
"context"
"fmt"
"time"
"github.com/go-redsync/redsync/v4"
"github.com/go-redsync/redsync/v4/redis/goredis/v9"
"github.com/redis/go-redis/v9"
)
// 初始化Redlock客户端(连接多个Redis节点)
func initRedSync() *redsync.Redsync {
// 创建多个Redis客户端(模拟3个独立节点)
client1 := redis.NewClient(&redis.Options{
Addr: "localhost:6379", // 节点1
})
client2 := redis.NewClient(&redis.Options{
Addr: "localhost:6380", // 节点2
})
client3 := redis.NewClient(&redis.Options{
Addr: "localhost:6381", // 节点3
})
// 将客户端包装为Redlock所需的池
pool1 := goredis.NewPool(client1)
pool2 := goredis.NewPool(client2)
pool3 := goredis.NewPool(client3)
// 初始化Redlock实例
return redsync.New(pool1, pool2, pool3)
}
// 使用Redlock执行分布式任务
func runDistributedTask(taskName string, taskFunc func()) error {
rs := initRedSync()
// 创建锁实例(锁key、过期时间、重试策略)
lock := rs.NewMutex(
taskName,
redsync.WithExpiry(10*time.Second), // 锁过期时间(防止死锁)
redsync.WithTries(3), // 获取锁的重试次数
redsync.WithRetryDelay(200*time.Millisecond), // 重试间隔
)
// 1. 获取分布式锁
ctx := context.Background()
if err := lock.LockContext(ctx); err != nil {
return fmt.Errorf("获取锁失败: %v", err)
}
fmt.Println("成功获取分布式锁")
// 2. 执行业务逻辑(临界区)
defer func() {
// 3. 释放锁(无论任务是否成功,确保锁释放)
if ok, err := lock.UnlockContext(ctx); !ok || err != nil {
fmt.Printf("释放锁失败: %v
", err)
} else {
fmt.Println("成功释放分布式锁")
}
}()
taskFunc() // 执行具体任务
return nil
}
func main() {
// 模拟分布式任务:打印任务执行信息
task := func() {
fmt.Println("开始执行分布式任务...")
time.Sleep(3 * time.Second) // 模拟任务耗时
fmt.Println("分布式任务执行完成")
}
// 执行任务(多实例运行时,只有一个实例能获取锁)
if err := runDistributedTask("distributed_task_lock", task); err != nil {
fmt.Printf("任务执行失败: %v
", err)
}
}
多Redis节点配置:
示例中配置了3个独立Redis节点(实际生产需部署在不同物理机/容器),Redlock要求节点间无主从复制,避免单点故障影响锁可靠性。
锁的关键参数:
WithExpiry(10*time.Second):锁的过期时间,需大于业务逻辑执行时间,防止死锁(若客户端崩溃,锁会自动过期)。
WithTries(3):获取锁的重试次数,避免因网络抖动导致的瞬时失败。
锁的获取与释放:
LockContext():底层执行Redlock算法,在多数节点上获取锁。
UnlockContext():通过Lua脚本原子验证并删除锁(确保只有持有锁的客户端能释放)。
临界区保护:
defer确保无论任务是否成功,锁都会被释放;业务逻辑(
taskFunc)在锁保护下执行,保证分布式环境下的唯一性。
lock.ExtendContext())。故障处理:若获取锁失败,需设置合理的重试策略(避免频繁重试),或降级处理。Lua脚本安全性:释放锁时必须验证随机值,避免误删其他客户端的锁(
redsync库已内置此逻辑)。
redsync)而非自行实现Redlock,避免算法细节漏洞。关键配置:合理设置锁的过期时间、重试次数,必要时实现锁续租,保证业务安全。
这套方案可满足分布式系统中对强一致性锁的需求,适用于秒杀、任务调度等核心场景。