Redis分佈式鎖
Redis可以用來實現分佈式鎖,有兩種實現方式:通過setnx實現的悲觀鎖和通過watch實現的樂觀鎖
悲觀鎖
package main
import (
"fmt"
"github.com/garyburd/redigo/redis"
"log"
"math/rand"
"strconv"
)
func main() {
conn, err := redis.Dial("tcp", "127.0.0.1:6379")
if err != nil {
log.Fatal(err)
}
tag := strconv.Itoa(rand.Int())
reply, err := conn.Do("set", "lock", tag, "ex", "1", "nx")
if reply == "OK" {
//do something
}
reply, err = conn.Do("eval",
"if redis.call('get', KEYS[1]) == ARGV[1] then " +
" return redis.call('del', KEYS[1])" +
"else" +
" return 0 " +
"end", "1", "lock", tag)
if reply == int64(1) {
fmt.Println("release lock")
}
}
本質就是通過set+nx
的方式只允許一個客戶端加鎖成功,主要的問題在於:
- 要注意給鎖加上超時,否則當客戶端斷了就再也沒人解鎖了
- 在解鎖的時候,注意只能刪除自己加的鎖,所以要做
delifequals
這樣的原子指令
樂觀鎖
package main
import (
"fmt"
"github.com/garyburd/redigo/redis"
"log"
)
func main() {
conn, err := redis.Dial("tcp", "127.0.0.1:6379")
if err != nil {
log.Fatal(err)
}
conn.Do("set", "lock", "1")
conn.Do("watch", "lock")
//conn.Do("set", "lock", "2")
conn.Do("multi")
conn.Do("set", "key", "123")
reply, err := conn.Do("exec")
fmt.Println(reply, err)
}
樂觀鎖則是利用watch+multi+exec
機制來實現,在exec
時會檢查watch
的值是否有發生變化