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
的值是否有发生变化
其他问题
在哨兵模式下,如果主节点挂掉就会将备用节点启动替换为主节点;
以上的这些问题就在于如果在主节点上获取了锁但是主节点还没有同步到备用节点上就挂掉了,那么就可能出现另外一个客户端过来加锁成功
网友评论