参考资料
- https://www.runoob.com/redis/redis-transactions.html -菜鸟教程
- https://www.liwenzhou.com/posts/Go/go_redis/#autoid-2-4-0 -李文周博客
安装
go get -u github.com/go-redis/redis连接
```go var rdb *redis.Client
func initClient() (err error) { rdb = redis.NewClient(&redis.Options{ Addr: “localhost:6379”, Password: “”, DB: 0, // 默认值 }) _, err = rdb.Ping().Result() return err }
<a name="lSpL6"></a>
# 基本操作
<a name="eE3yP"></a>
## set、get
```go
func main() {
err := initClient()
if err != nil {
fmt.Printf("initclinet failed, err: %v\n", err)
}
// key, value , 超时时间
err = rdb.Set("score", 100, 0).Err()
if err != nil {
fmt.Printf("set failed, %v\n", err)
}
val, err := rdb.Get("score").Result()
if err != nil {
fmt.Printf("get failed, %v\n", err)
} else {
fmt.Println(val)
}
val2, err := rdb.Get("name").Result()
if err == redis.Nil {
fmt.Println("name does not exist")
} else if err != nil {
fmt.Printf("get name failed, err:%v\n", err)
return
} else {
fmt.Println("name:", val2)
}
}
zset
func redisExample2() {
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++"},
}
// ZADD
num, err := rdb.ZAdd(zsetKey, languages...).Result()
if err != nil {
fmt.Printf("zadd failed, err:%v\n", err)
return
}
fmt.Printf("zadd %d succ.\n", num)
// 把Golang的分数加10
newScore, err := rdb.ZIncrBy(zsetKey, 10.0, "Golang").Result()
if err != nil {
fmt.Printf("zincrby failed, err:%v\n", err)
return
}
fmt.Printf("Golang's score is %f now.\n", newScore)
// 取分数最高的3个
ret, err := rdb.ZRevRangeWithScores(zsetKey, 0, 2).Result()
if err != nil {
fmt.Printf("zrevrange failed, err:%v\n", err)
return
}
for _, z := range ret {
fmt.Println(z.Member, z.Score)
}
// 取95~100分的
op := redis.ZRangeBy{
Min: "95",
Max: "100",
}
ret, err = rdb.ZRangeByScoreWithScores(zsetKey, op).Result()
if err != nil {
fmt.Printf("zrangebyscore failed, err:%v\n", err)
return
}
for _, z := range ret {
fmt.Println(z.Member, z.Score)
}
}
Pipeline
使用pipeline的目的是进行网络优化:如果有数量很多的redis服务请求,如果每个请求都单独发送,则将耗费大量RTT(Round-Trip Time),而使用pipeline,将使大量请求一次性发送,只耗费一个RTT。
适用场景:
需要连续发送多个相互之间没有依赖的数据库指令。
// 基本操作
pipe := rdb.Pipeline()
incr := pipe.Incr("pipeline_counter")
pipe.Expire("pipeline_counter", time.Hour)
_, err := pipe.Exec()
fmt.Println(incr.Val(), err)
// 使用pipelined
var incr *redis.IntCmd
_, err := rdb.Pipelined(func(pipe redis.Pipeliner) error {
incr = pipe.Incr("pipelined_counter")
pipe.Expire("pipelined_counter", time.Hour)
return nil
})
fmt.Println(incr.Val(), err)
// 相当于一次性执行了下面的命令:
// INCR pipeline_counter
// EXPIRE pipeline_counts 3600
事务
Redis 事务可以一次执行多个命令, 并且带有以下三个重要的保证:
- 批量操作在发送 EXEC 命令前被放入队列缓存。
- 收到 EXEC 命令后进入事务执行,事务中任意命令执行失败,其余的命令依然被执行。
- 在事务执行过程,其他客户端提交的命令请求不会插入到事务执行命令序列中。
pipe := rdb.TxPipeline()
incr := pipe.Incr("tx_pipeline_counter")
pipe.Expire("tx_pipeline_counter", time.Hour)
_, err := pipe.Exec()
fmt.Println(incr.Val(), err)
// 相当于执行了:
// MULTI
// INCR pipeline_counter
// EXPIRE pipeline_counts 3600
// EXEC
watch
在某些场景下,我们除了要使用MULTI/EXEC命令外,还需要配合使用WATCH命令。用户使用WATCH命令监视某个键之后,直到该用户执行EXEC命令的这段时间里,如果有其他用户抢先对被监视的键进行了更改,那么当用户尝试执行EXEC的时候,事务将失败并返回一个错误,用户可以根据这个错误选择重试事务或者放弃事务。
// 监视watch_count的值,并在值不变的前提下将其值+1
key := "watch_count"
err = client.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)