1 Redis 管道
正常的情况下,redis是请求响应模式,一条请求后那么正常就会返回一个响应,例如上图。但是只存在这种情况是无法满足我们开发的需求的。所以redis给我们提供了管道。
redis的管道(pipeline )相关特点:
- 1)redis的管道pipeline配合事务,可以实现原子操作,保证一次执行多条命令。注意:管道本身不具有原子操作,只是单纯提高传输性能,而原子操作还是由事务和lua脚本实现。
- 2)它一次可以发送多次请求,然后返回多个响应,这个响应的顺序会依照你请求的顺序进行返回。
- 3)管道作用:一次可以发送多条请求,并可以一次返回多个响应,例如下图,所以节省大量的网络耗时,减少与redis的交互,主要体现在TCP三次握手与四次挥手。如果将多个请求拆开,势必会效率大打折扣。
- 4)redis pipeline 是一个由客户端提供的,而不是服务端提供的。
下面我们来看一下redigo提供了哪些接口给我们。
type Conn interface {
Close() error
Err() error
Do(commandName string, args ...interface{}) (reply interface{}, err error)
Send(commandName string, args ...interface{}) error
Flush() error
Receive() (reply interface{}, err error)
}
重点讲解Do和Send、Flush、Receive的关系。
- 1)Do等价于执行了一次Send、Flush、Receive。
- 2)Send函数代表往redigo的缓冲区发送消息,相当于写到缓冲区,此时并未发送到redis服务器。
- 3)Flush函数代表真正的将redigo的缓冲区的内容往redis服务器发送。
- 4)Receive函数代表从redis服务器中接收返回的信息。
2 管道作用
redis的主要作用上面也说过,就是节省大量的网络耗时,减少与redis的交互,主要体现在TCP三次握手与四次挥手。
3 管道使用技巧
// 1. 批量发送,批量接收。
c.Send(cmd1, ...)
c.Send(cmd2, ...)
c.Send(cmd3, ...)
c.Flush() // 将上面的三个命令发送出去
c.Receive() // cmd1 的返回值
c.Receive() // cmd2 的返回值
c.Receive() // cmd3 的返回值
// 2. 如果不需要关注返回值。
c.Send(cmd1, ...)
c.Send(cmd2, ...)
c.Send(cmd3, ...)
c.Do("")
// 简单分析Do(""):
// Do=Send、Flush、Receive。而此时传空字符串,那么此时的Send相当于没命令即没意义,
// 那么此时的Do("")等价于连续执行了c.Flush()与c.Receive(),并不关心返回值。
// 3. 如果只关注最后一个命令的返回值。
// 一般这种情况前两条是写,第三条语句是读。
c.Send(cmd1, ...)
c.Send(cmd2, ...)
c.Do(cmd3, ...)
但是注意,上面每次调用函数后,都应该去判断返回值,养成良好的习惯,不是说忽略了redis的返回值就不判断错误。 例如忽略返回值但判断错误情况:
ifi( _, err := c.Do("")); err != nil{
}
4 管道代码例子
package main
import (
"fmt"
"math/rand"
"time"
"github.com/garyburd/redigo/redis"
)
func main() {
c, err := redis.Dial("tcp", "192.168.1.9:6379", redis.DialPassword("123456"))
if err != nil {
panic(err)
}
defer (func() {
fmt.Println("connection close")
c.Close()
})()
if false {
c.Send("del", "set", "list", "zset")
c.Send("sadd", "set", "tyy", "hc", "lqq")
c.Send("lpush", "list", 10001, 10002, 10003)
c.Send("smembers", "set")
c.Send("lrange", "list", 0, -1)
c.Flush()
c.Receive()
c.Receive()
c.Receive()
mbrs, err := redis.Strings(c.Receive())
if err != redis.ErrNil {
fmt.Println(mbrs)
}
lsts, err := redis.Ints(c.Receive())
if err != redis.ErrNil {
fmt.Println(lsts)
}
}
if false {
c.Send("del", "set", "list", "zset")
c.Send("sadd", "set", "tyy", "hc", "lqq")
c.Send("lpush", "list", 10001, 10002, 10003)
c.Do("")
}
if false {
rand.Seed(time.Now().UnixNano())
c.Send("del", "set", "list", "zset")
c.Send("sadd", "set", "tyy", "hc", "lqq")
{
args := redis.Args{}.Add("zset")
args = args.Add(rand.Intn(100)).Add("tyy")
args = args.Add(rand.Intn(100)).Add("hc")
args = args.Add(rand.Intn(100)).Add("lqq")
c.Send("zadd", args...)
}
{
args := redis.Args{}.Add("zset")
args = args.Add(0).Add(-1).Add("withscores")
vals, err := redis.Values(c.Do("zrange", args...))
if err != nil {
panic(err)
}
var rets []struct {
Name string
Score int
}
if err = redis.ScanSlice(vals, &rets); err != nil {
panic(err)
}
fmt.Println(rets)
}
}
}
测试方法:将上面的3个if添加中的false逐个改成true即可测得下面结果。 分别对应上面的结果:
|