基于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 } } } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理