参考资料
- 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```gofunc 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++"},}// ZADDnum, 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的分数加10newScore, 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)// 使用pipelinedvar 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的值,并在值不变的前提下将其值+1key := "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)
