分布式的业务中共享资源需要安全的被访问和处理 , 就需要分布式锁
-
「锁的互斥性」:在分布式集群应用中共享资源的锁在同一时间只能被一个对象获取。
-
「可重入」:为了避免死锁,这把锁是可以重入的并且可以设置超时。
-
「高效的加锁和解锁」:能够高效的加锁和解锁,获取锁和释放锁的性能也好。
-
「阻塞、公平」:可以根据业务的需要,考虑是使用阻塞还是非阻塞,公平还是非公平的锁。
redis实现分布式锁主要靠SET key value EX * NX
命令
-
当key存在时写入失败 , 保证互斥性
-
设置了key超时 , 避免死锁
-
利用mutex保证当前程序不存在并发冲突问题
-
此方法弊端是对超时时间的设置有要求,需要根据具体业务设置一个合理的经验值,避免锁超时时间到了,业务没执行完的问题。
代码方案1:
package redis
import (
"context"
"log"
"sync"
"time"
"github.com/go-redis/redis/v8"
)
var rdb *redis.Client
var ctx = context.Background()
var mutex sync.Mutex
func NewRedis() {
rdb = redis.NewClient(&redis.Options{
Addr: "192.168.1.7:6379",
Password: "", // no password set
DB: 0, // use default DB
})
}
func Lock(key string) bool {
mutex.Lock()
defer mutex.Unlock()
//SET key value EX 10 NX
bool, err := rdb.SetNX(ctx, key, 1, 10*time.Second).Result()
if err != nil {
log.Println(err.Error())
}
return bool
}
func UnLock(key string) int64 {
nums, err := rdb.Del(ctx, key).Result()
if err != nil {
log.Println(err.Error())
return 0
}
return nums
}
代码方案2:
package mian
import (
"fmt"
"time"
"github.com/gomodule/redigo/redis"
)
func main(){
rds, err := redis.Dial("tcp", "192.168.1.7:6379")
if err != nil {
fmt.Println("Fail to conn redis: ", err)
return
}
defer rds.Close()
for {
ret, err := rds.Do("SET", "lock", 1, "EX", 5, "NX")
if err != nil {
fmt.Println("Fail to set lock: ", err)
}
ret, err = redis.String(ret, err)
//加锁失败
if ret != "OK"{
fmt.Println("Fail to lock")
time.Sleep(5 * time.Second)
continue
}
//加锁成功
fmt.Println("work start...")
fmt.Println("work end...")
//业务处理结束后释放锁
ret, err := rds.Do("del", "lock")
break;
}
}