redis 分布式锁

完整代码传送门

go 复制代码
package redis_distributed_lock

import (
	"context"
	"fmt"
	"redis-distributed-lock/redis_client"
	"runtime"
	"strings"
	"time"

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

type RedisDistributedLock struct {
	client     *redis.Client
	lockKey    string
	lockValue  string
	expireTime int
}

const redisLockKey = "redisLock"
const redisLockExpireTime = 30

func NewRedisDistributedLock(client *redis.Client) *RedisDistributedLock {
	return &RedisDistributedLock{
		client:     client,
		lockKey:    redisLockKey,
		lockValue:  GoID(),
		expireTime: redisLockExpireTime,
	}
}

func (r *RedisDistributedLock) Lock() {
	script := `
if redis.call("exists", KEYS[1]) == 0 or redis.call("hexists", KEYS[1], ARGV[1]) == 1 then
	redis.call("hincrby", KEYS[1], ARGV[1], 1)
	redis.call("expire", KEYS[1], ARGV[2])
	return 1
else
	return 0
end`
	client := redis_client.NewClient()
	for {
		val, err := client.Eval(context.Background(), script, []string{r.lockKey}, r.lockValue, r.expireTime).Result()
		if err != nil {
			fmt.Printf("lock failed, err: %v\n", err)
			return
		}
		if val != int64(1) {
			time.Sleep(30 * time.Millisecond)
		} else {
			fmt.Printf("lock successfully, lockName: %v, lockValue: %v\n", r.lockKey, r.lockValue)
			go r.renewExpire()
			break
		}
	}
}

func (r *RedisDistributedLock) renewExpire() {
	script := `
if redis.call("hexists", KEYS[1], ARGV[1]) == 1 then
	return redis.call("expire", KEYS[1], ARGV[2])
else
	return 0
end`
	for {
		time.Sleep(time.Duration(r.expireTime/3) * time.Second)
		val, err := r.client.Eval(context.Background(), script, []string{r.lockKey}, r.lockValue, r.expireTime).Result()
		if err != nil {
			fmt.Printf("renewExpire failed, err: %v\n", err)
			return
		}
		if val == int64(0) {
			return
		}
	}
}

func (r *RedisDistributedLock) Unlock() {
	script := `
if redis.call("hexists", KEYS[1], ARGV[1]) == 0 then
	return nil
elseif redis.call("hincrby", KEYS[1], ARGV[1], -1) == 0 then
	return redis.call("del", KEYS[1])
else
	return 0
end`
	client := redis_client.NewClient()
	val, err := client.Eval(context.Background(), script, []string{r.lockKey}, r.lockValue).Result()
	if err != nil {
		fmt.Printf("unlock failed, err: %v\n", err)
		return
	}
	if val == redis.Nil {
		fmt.Printf("lock does not exist\n")
		return
	}
	fmt.Printf("unlock successfully, lockName: %v, lockValue: %v\n", r.lockKey, r.lockValue)
}

func GoID() string {
	var buf [64]byte
	n := runtime.Stack(buf[:], false)
	return strings.Fields(strings.TrimPrefix(string(buf[:n]), "goroutine "))[0]
}
相关推荐
江湖十年9 分钟前
MCP 官方 Go SDK v1.0.0 正式发布:Go 生态的模型上下文协议步入稳定时代
人工智能·后端·go
Coding君1 小时前
每日一Go-39、Go 内存分配器深度拆解--Arena /Span / MSpan / 大对象 / 小对象
go
JustMove0n1 小时前
互联网大厂Java面试全流程问答及技术详解
java·jvm·redis·mybatis·dubbo·springboot·多线程
Bug养殖户2 小时前
go语言http解析(二)路由树解析与注册
go
java1234_小锋11 小时前
Java高频面试题:Redis的Key和Value的设计原则有哪些?
java·redis·面试
Javatutouhouduan11 小时前
SpringBoot整合reids:JSON序列化文件夹操作实录
java·数据库·redis·html·springboot·java编程·java程序员
java1234_小锋12 小时前
Java高频面试题:Redis里什么是缓存击穿、缓存穿透、缓存雪崩?
java·redis·缓存
y = xⁿ13 小时前
【从零开始学习Redis|第四篇】从底层理解缓存问题:雪崩、击穿、穿透与一致性设计
java·redis·学习·缓存
代码探秘者13 小时前
【Redis】分布式锁深度解析:实现、可重入、主从一致性与强一致方案
java·数据库·redis·分布式·缓存·面试
霖霖总总13 小时前
[Redis小技巧5]Redis Sorted Set 深度解析:从跳表原理到亿级排行榜架构
redis·架构