基于redis的分布式锁

1. redis分布式锁原理

redis实现分布式锁其实就是对key进行操作

  • 加锁:给对应的lockkey赋值,就进行了加锁操作,其他进程发现该lockkey已经值了有值了,就说明锁已经被别人获取
  • 解锁:获取到锁的进程删除这个lockkey
  • 锁超时:避免客户端宕机导致无法正常释放锁

 

1.1 加锁

加锁实际上就是在redis中,给lockkey设置一个值,为了避免死锁,给定一个过期时间:

SET lock_key uuid NX PX 5000

上面的操作就是给lock_key设置了一个uuid,并且过期时间为5000ms,如果执行成功就说明获取到了锁

 

1.2 解锁

解锁的过程就是将lock_key删除。但也不能乱删,也就是不能删掉其他客户端获取到的锁。这时候uuid的作用就体现出来

为了保证解锁操作的原子性,可以用LUA脚本完成这一操作。先判断当前锁的字符串是否与传入的值相等,是的话就删除Key,解锁成功

if redis.call('get',KEYS[1])==ARGV[1] then
return redis.call('del',KEYS[1])
else
return 0
end

 

2. go实现redis分布式锁

首先需要引入第三方依赖:

go get github.com/gomodule/redigo/redis
go get github.com/satori/go.uuid

 

获取到redis连接池完成初始化

var redisPool *redis.Pool
func init() {
redisPool = getRedisPool()
}
// getRedisPool 获取redis连接池
func getRedisPool() *redis.Pool {
return &redis.Pool{
MaxIdle: 1,
MaxActive: 10,
IdleTimeout: 180 * time.Second,
Dial: func() (redis.Conn, error) {
c, err := redis.Dial("tcp", "xx.xx.xx.xx:6379")
if err != nil {
return nil, err
}
// 选择db
c.Do("SELECT", 0)
return c, nil
},
}
}

 

定义我们分布式锁的结构体

type Locker struct {
Key string
Uuid string
Error error
}

 

尝试获取到分布式锁,带有超时机制

// TryLock: 尝试锁,带尝试超时机制
func TryLock(key string, timeout time.Duration) (locker *Locker) {
// 获取uuid
response, err := uuid.NewV4()
if err != nil {
fmt.Println("get uuid failed")
return
}
uid := response.String()
locker = &Locker{
Key: key,
Uuid: uid,
}
// 开始尝试获取锁
start := time.Now()
for time.Now().Sub(start) < timeout {
redisConn := redisPool.Get()
reply, _ := redis.String(redisConn.Do("SET", locker.Key, locker.Uuid, "EX", 60, "NX"))
redisConn.Close()
if reply == "OK" {
return
}
time.Sleep(time.Duration(200) * time.Millisecond)
}
locker.Error = errors.New("try to lock timeout")
return
}

 

业务代码结束之后可以释放锁

func (locker *Locker) Unlock() {
// 只有获取到锁的时候才可以释放锁
if lock.Error == nil {
redisConn := redisPool.Get()
defer redisConn.Close()
reply, err := redisConn.Do("GET", locker.Key)
if err != nil {
fmt.Println("redis connect failed")
return
}
redisUUID := reply.([]byte)
// 确定这是自己的锁
if string(redisUUID) == locker.Uuid {
reply, _ := redisConn.Do("DEL", locker.Key)
if reply != "OK" {
fmt.Println("delete key failed")
return
}
}
}
}

  

posted @   aganippe  阅读(171)  评论(0编辑  收藏  举报
(评论功能已被禁用)
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
点击右上角即可分享
微信分享提示