分佈式的業務中共享資源需要安全的被訪問和處理 , 就需要分佈式鎖
-
「鎖的互斥性」:在分佈式集羣應用中共享資源的鎖在同一時間只能被一個對象獲取。
-
「可重入」:爲了避免死鎖,這把鎖是可以重入的並且可以設置超時。
-
「高效的加鎖和解鎖」:能夠高效的加鎖和解鎖,獲取鎖和釋放鎖的性能也好。
-
「阻塞、公平」:可以根據業務的需要,考慮是使用阻塞還是非阻塞,公平還是非公平的鎖。
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;
}
}