安装
go get -u github.com/go-redis/redis
连接
- 普通连接
func NewClient() (c *redis.Client, err error) {
c = redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "",
DB: 0,
})
_, err = c.Ping().Result()
if err != nil {
return nil, err
}
return
}
- 连接redis哨兵模式
func NewClient() (c *redis.Client, err error) {
c = redis.NewFailoverClient(&redis.FailoverOptions{
MasterName:"master",
SentinelAddrs:[]string{"xxxx:26379","xxxx:26379"},
})
_, err = c.Ping().Result()
if err != nil {
return nil, err
}
return
}
- 连接redis集群
func NewClient() (c *redis.ClusterClient, err error) {
c = redis.NewClusterClient(&redis.ClusterOptions{
Addrs: []string{":7001", ":7002", ":7003"},
})
_, err = c.Ping().Result()
if err != nil {
return nil, err
}
return
}
基本使用
set/get
//go get -u github.com/sirupsen/logrus
func printErr(err error) {
if err != nil {
logrus.Errorf("err:%+v\n", err)
}
}
func SetGetEx() {
c, err := NewClient()
if err != nil {
logrus.Errorf("NewClient err:%+v", err)
return
}
err = c.Set("score", 100, 0).Err()
printErr(err)
val, err := c.Get("score").Result()
printErr(err)
logrus.Println("score", val)
c.Del("name")
val2, err := c.Get("name").Result()
if err == redis.Nil {
logrus.Println("name does not exist")
} else if err != nil {
logrus.Errorf("get name failed err:%+v\n")
} else {
logrus.Println("name:", val2)
}
}
zset
func ZsetEx() {
c, err := NewClient()
if err != nil {
logrus.Errorf("NewClient err:%+v", err)
return
}
zsetKey := "language_rank"
languages := []redis.Z{
redis.Z{Score: 90.0, Member: "Golang"},
redis.Z{Score: 98.0, Member: "Java"},
redis.Z{Score: 95.0, Member: "Python"},
redis.Z{Score: 97.0, Member: "JavaScript"},
redis.Z{Score: 99.0, Member: "C/C++"},
}
num, err := c.ZAdd(zsetKey, languages...).Result()
printErr(err)
logrus.Printf("zadd %d succ.\n", num)
//golang + 10分
newScore, err := c.ZIncrBy(zsetKey, 10.0, "Golang").Result()
printErr(err)
logrus.Println("Golang score ", newScore)
//取前三 从小到大排序
rets, err := c.ZRangeWithScores(zsetKey, 0, 2).Result()
printErr(err)
for _, v := range rets {
logrus.Println(v.Member, v.Score)
}
//取95~100分间的
op := redis.ZRangeBy{
Min: "95",
Max: "100",
}
rets, err = c.ZRangeByScoreWithScores(zsetKey, op).Result()
printErr(err)
for _, v := range rets {
logrus.Println(v.Member, v.Score)
}
}
pipeline
Pipeline 主要是一种网络优化。它本质上意味着客户端缓冲一堆命令并一次性将它们发送到服务器。这些命令不能保证在事务中执行。这样做的好处是节省了每个命令的网络往返时间(RTT)
func PipelineEx() {
c, err := NewClient()
if err != nil {
logrus.Errorf("NewClient err:%+v", err)
return
}
pipe := c.Pipeline()
incr := pipe.Incr("pipeline_cnt")
pipe.Expire("pipeline_cnt", time.Hour)
_, err = pipe.Exec()
printErr(err)
logrus.Println("incr.Val() ", incr.Val())
}
func PipelinedEx() {
c, err := NewClient()
if err != nil {
logrus.Errorf("NewClient err:%+v", err)
return
}
_, err = c.Pipelined(func(pipe redis.Pipeliner) error {
incr := pipe.Incr("pipeline_cnt")
pipe.Expire("pipeline_cnt", time.Hour)
logrus.Println("incr.Val() ", incr.Val())
return nil
})
printErr(err)
}
multi/exec
Redis是单线程的,因此单个命令始终是原子的,但是来自不同客户端的两个给定命令可以依次执行,例如在它们之间交替执行。但是,MULTI/EXEC能够确保在MULTI/EXEC两个语句之间的命令之间没有其他客户端正在执行命令。
在这种场景我们需要使用TxPipeline。TxPipeline总体上类似于上面的Pipeline,但是它内部会使用MULTI/EXEC包裹排队的命令。
func TxPipelineEx() {
c, err := NewClient()
if err != nil {
logrus.Errorf("NewClient err:%+v", err)
return
}
pipe := c.TxPipeline()
incr := pipe.Incr("pipeline_cnt")
pipe.Expire("pipeline_cnt", time.Hour)
_, err = pipe.Exec()
printErr(err)
logrus.Println("incr.Val() ", incr.Val())
}
func TxPipelinedEx() {
c, err := NewClient()
if err != nil {
logrus.Errorf("NewClient err:%+v", err)
return
}
_, err = c.TxPipelined(func(pipe redis.Pipeliner) error {
incr := pipe.Incr("pipeline_cnt")
pipe.Expire("pipeline_cnt", time.Hour)
logrus.Println("incr.Val() ", incr.Val())
return nil
})
printErr(err)
}
watch
在某些场景下,我们除了要使用MULTI/EXEC命令外,还需要配合使用WATCH命令。在用户使用WATCH命令监视某个键之后,知道该用户执行EXEC命令的这段时间里,如果有其他用户抢先对被监视的键进行了替换、更新、删除等操作,那么当用户尝试执行EXEC的时候,事务将失败并返回一个错误,用户可以根据这个错误选择重试事务或者放弃事务
Watch(fn func(*Tx) error, keys ...string) error
func WatchEx() {
c, err := NewClient()
if err != nil {
logrus.Errorf("NewClient err:%+v", err)
return
}
key := "watch_count"
err = c.Watch(func(tx *redis.Tx) error {
n, err := tx.Get(key).Int()
if err != nil && err != redis.Nil {
return err
}
_, err = tx.Pipelined(func(pipe redis.Pipeliner) error {
pipe.Set(key, n+1, 0)
return nil
})
return err
}, key)
printErr(err)
}
PS:官方文档中使用GET和SET命令以事务方式递增Key的值的示例
const routineCount = 100
increment := func(key string) error {
txf := func(tx *redis.Tx) error {
// 获得当前值或零值
n, err := tx.Get(key).Int()
if err != nil && err != redis.Nil {
return err
}
// 实际操作(乐观锁定中的本地操作)
n++
// 仅在监视的Key保持不变的情况下运行
_, err = tx.Pipelined(func(pipe redis.Pipeliner) error {
// pipe 处理错误情况
pipe.Set(key, n, 0)
return nil
})
return err
}
for retries := routineCount; retries > 0; retries-- {
err := rdb.Watch(txf, key)
if err != redis.TxFailedErr {
return err
}
// 乐观锁丢失
}
return errors.New("increment reached maximum number of retries")
}
var wg sync.WaitGroup
wg.Add(routineCount)
for i := 0; i < routineCount; i++ {
go func() {
defer wg.Done()
if err := increment("counter3"); err != nil {
fmt.Println("increment error:", err)
}
}()
}
wg.Wait()
n, err := rdb.Get("counter3").Int()
fmt.Println("ended with", n, err)
版权声明:本文为u013246898原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。