泽兴芝士网

一站式 IT 编程学习资源平台

Redis如何实现分布式锁

在 Go 语言里,可以借助 Redis 实现分布式锁,以下为你介绍几种常见的实现方式。

1. 使用SET命令

Redis 2.6.12 版本之后,SET 命令支持 NX(键不存在时设置)和 EX(设置过期时间)选项,能原子性地完成设置键值和过期时间的操作,从而避免死锁。

go

package main

import (
	"context"
	"fmt"
	"time"

	"github.com/go-redis/redis/v8"
)

// 初始化 Redis 客户端
var redisClient = redis.NewClient(&redis.Options{
	Addr:     "localhost:6379",
	Password: "",
	DB:       0,
})

// AcquireLock 尝试获取锁
func AcquireLock(ctx context.Context, lockKey string, lockValue string, expiration time.Duration) bool {
	set, err := redisClient.SetNX(ctx, lockKey, lockValue, expiration).Result()
	if err != nil {
		fmt.Printf("获取锁时发生错误: %v\n", err)
		return false
	}
	return set
}

// ReleaseLock 释放锁
func ReleaseLock(ctx context.Context, lockKey string) {
	err := redisClient.Del(ctx, lockKey).Err()
	if err != nil {
		fmt.Printf("释放锁时发生错误: %v\n", err)
	}
}

func main() {
	ctx := context.Background()
	lockKey := "my_distributed_lock"
	lockValue := "unique_value"
	expiration := 10 * time.Second

	if AcquireLock(ctx, lockKey, lockValue, expiration) {
		defer ReleaseLock(ctx, lockKey)
		fmt.Println("获取到锁,执行关键操作...")
		// 模拟关键操作
		time.Sleep(5 * time.Second)
		fmt.Println("关键操作执行完毕,释放锁")
	} else {
		fmt.Println("未能获取到锁")
	}
}

代码解释

  • 初始化 Redis 客户端:利用 github.com/go-redis/redis/v8 库创建一个 Redis 客户端。
  • AcquireLock 函数:借助 SetNX 方法尝试获取锁,若键不存在则设置成功并返回 true,同时设置过期时间以避免死锁。
  • ReleaseLock 函数:使用 Del 方法删除键,从而释放锁。
  • main 函数:尝试获取锁,若成功则执行关键操作,最后释放锁;若失败则输出提示信息。

2. 使用 Redlock 算法(多节点 Redis)

在多节点 Redis 场景下,可使用 Redlock 算法增强分布式锁的可靠性。

go

package main

import (
	"context"
	"fmt"
	"sync"
	"time"

	"github.com/go-redis/redis/v8"
)

// 初始化多个 Redis 客户端
var redisClients = []*redis.Client{
	redis.NewClient(&redis.Options{Addr: "localhost:6379", Password: "", DB: 0}),
	redis.NewClient(&redis.Options{Addr: "localhost:6380", Password: "", DB: 0}),
	redis.NewClient(&redis.Options{Addr: "localhost:6381", Password: "", DB: 0}),
}

// AcquireRedlock 尝试获取 Redlock
func AcquireRedlock(ctx context.Context, lockKey string, lockValue string, expiration time.Duration) bool {
	var wg sync.WaitGroup
	successCount := 0
	for _, client := range redisClients {
		wg.Add(1)
		go func(c *redis.Client) {
			defer wg.Done()
			set, err := c.SetNX(ctx, lockKey, lockValue, expiration).Result()
			if err == nil && set {
				successCount++
			}
		}(client)
	}
	wg.Wait()
	return successCount > len(redisClients)/2
}

// ReleaseRedlock 释放 Redlock
func ReleaseRedlock(ctx context.Context, lockKey string) {
	for _, client := range redisClients {
		err := client.Del(ctx, lockKey).Err()
		if err != nil {
			fmt.Printf("释放 Redlock 时发生错误: %v\n", err)
		}
	}
}

func main() {
	ctx := context.Background()
	lockKey := "my_redlock"
	lockValue := "unique_value"
	expiration := 10 * time.Second

	if AcquireRedlock(ctx, lockKey, lockValue, expiration) {
		defer ReleaseRedlock(ctx, lockKey)
		fmt.Println("获取到 Redlock,执行关键操作...")
		// 模拟关键操作
		time.Sleep(5 * time.Second)
		fmt.Println("关键操作执行完毕,释放 Redlock")
	} else {
		fmt.Println("未能获取到 Redlock")
	}
}

代码解释

  • 初始化多个 Redis 客户端:创建多个 Redis 客户端,分别连接不同的 Redis 节点。
  • AcquireRedlock 函数:并发地在多个 Redis 节点上尝试获取锁,统计成功获取锁的节点数量,若超过半数则认为获取锁成功。
  • ReleaseRedlock 函数:在所有 Redis 节点上释放锁。
  • main 函数:尝试获取 Redlock,若成功则执行关键操作,最后释放锁;若失败则输出提示信息。
控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言