Go访问Redis
Redis连接
Go语言官方并没有提供Redis访问包。在Redis官网上有很多Go语言的客户端包,它们都能实现对Redis的访问和操作。
相对来说Rdig()使用起来更人性化。重要的是,其源代码结构很清晰,而且其支持管道、发布和订阅、连接池等。所以选择Redigo作为示例讲解。
- Redis连接
获取项目包,命令行输入:
go get github.com/gomodule/redigo
import (
"fmt"
"github.com/gomodule/redigo/redis"
)
/*
用redis.Dial()函数连接Redis服务器
*/
func main() {
c, err := redis.Dial("tcp", "localhost:6379",
redis.DialPassword("123456"),
redis.DialDatabase(1))
if err != nil {
fmt.Println("conn redis failed, err:", err)
return
}
defer c.Close()
}
- Redis设置和获取字符串
Redigo客户端包中最常用的是Do()方法,它可以直接支持Redis的Set、Get、MSet、MGet、HSet、HGet等常用命令。下面示例代码是通过调用Do()方法来设置字符串:
//设置字符串
_, err = c.Do("Set", "username", "jack")
if err != nil {
fmt.Println(err)
return
}
通过redis.String()函数来获取字符串:
//获取字符串
res, err := redis.String(c.Do("get", "username"))
if err != nil {
fmt.Println(err)
return
}
fmt.Println(res)
- 批量设置
在Redigo客户端包中,可以用Do()方法来批量设置字符串;可以用redis.Strings()函数配合Do()方法来批量获取字符串:
//批量设置字符串
_, err = c.Do("MSet", "username", "james", "phone", "18812345678")
if err != nil {
fmt.Println("MSet error:", err)
return
}
//批量获取字符串
res2, err := redis.Strings(c.Do("MGet", "username", "phone"))
if err != nil {
fmt.Println(err)
return
}
fmt.Println(res2)
- Redis hash操作
在Redigo客户端包中,可以用Do()方法来设置和获取hash类型:
//hash操作
_, err = c.Do("HSet", "names", "jim", "barry")
if err != nil {
fmt.Println(err)
return
}
res3, err := redis.String(c.Do("HGet", "names", "jim"))
if err != nil {
fmt.Println("hget error:", err)
return
}
fmt.Println(res3)
5.Redis设置过期时间
在Redigo客户端包中,可以用Do()方法来设置过期时间:
//设置过期时间
_, err = c.Do("expire", "names", 10)
if err != nil {
fmt.Println("expire error:", err)
return
}
- Redis队列
//队列
_, err = c.Do("lpush", "Queue", "jim", "barry", 9)
if err != nil {
fmt.Println("lpush error:", err)
return
}
for {
r, err := redis.String(c.Do("lpop", "Queue"))
if err != nil {
fmt.Println("lpop error:", err)
break
}
fmt.Println(r)
}
res4, err := redis.Int(c.Do("llen", "Queue"))
if err != nil {
fmt.Println(err)
return
}
fmt.Println(res4)
- 实现Redis连接池功能
为什么使用连接池?Redis也是一种数据库,它基于C/S模式,因此如果需要使用,则必须先建立连接。C/S模式就是一种远程通信的交互模式,因此Redis服务器可以单独作为一个数据库服务器独立存在。
假设Redis服务器与客户端分处异地,虽然基于内存的Redis数据库有着超高的性能,但是底层的网络通信却占用了一次数据请求的大量时间。因为,每次数据交互都需要先建立连接。假设一次数据交互总共用时30ms,超高性能的Redis数据库处理数据所花的时间可能不到1ms,也就是说前期的连接占用了29ms。
连接池则可以实现在客户端建立多个与服务器的连接并且不释放。当需要使用连接时,通过一定的算法获取已经建立的连接,使用完后则还给连接池,这就免去了连接服务器所占用的时间。
Redigo客户端包中通过Pool对象来建立连接池,其使用方法如下。
1、使用Pool结构体初始化一个连接池
pool = &redis.Pool{
MaxIdle: 16,
MaxActive: 1024,
IdleTimeout: 300,
Dial: func() (redis.Conn, error) {
return redis.Dial("tcp", "localhost:6379", redis.DialPassword("123456"))
},
}
该结构体各字段的解释如下:
- Maxldle:最大的空闲连接数,表示即使在没有Redis连接时,依然可以保持n个空闲的连接,随时处于待命状态。
- MaxActive:最大的激活连接数,表示同时最多有n个连接。
- IdleTimeout:最大的空闲连接等待时间,超过此时间后空闲连接将被关闭。
2、调用Do()方法来设置和获取字符串
import (
"fmt"
"github.com/gomodule/redigo/redis"
)
/*
使用连接池
*/
var pool *redis.Pool
func init() {
pool = &redis.Pool{
MaxIdle: 16,
MaxActive: 1024,
IdleTimeout: 300,
Dial: func() (redis.Conn, error) {
return redis.Dial("tcp", "localhost:6379", redis.DialPassword("123456"))
},
}
}
func main() {
c := pool.Get()
defer c.Close()
_, err := c.Do("Set", "username", "jack")
if err != nil {
fmt.Println(err)
return
}
r, err := redis.String(c.Do("Get", "username"))
if err != nil {
fmt.Println(err)
return
}
fmt.Println(r)
}
- Redis实现管道操作
请求/响应服务可以实现持续处理新请求。客户端可以发送多个命令到服务器端而无须等待响应,最后再一次性读取多个响应。
Send()、Flush()、Receive()方法支持管道化操作。Send()方法用于向连接的输出缓冲中写入命令。Flush()方法用于将连接的输出缓冲清空并写入服务器端。Recevie()方法用于按照FIFO顺序依次读取服务器端的响应。示例代码如下。
/*
管道操作
*/
func main() {
c, err := redis.Dial("tcp", "localhost:6379",
redis.DialPassword("123456"),
redis.DialDatabase(0))
if err != nil {
fmt.Println(err)
return
}
defer c.Close()
c.Send("Set", "username1", "jim")
c.Send("Set", "username2", "jack")
c.Flush()
v, err := c.Receive()
fmt.Printf("v:%v, err:%v\n", v, err)
v, err = c.Receive()
fmt.Printf("v:%v, err:%v\n", v, err)
v, err = c.Receive() //一直等待
fmt.Printf("v:%v, err:%v\n", v, err)
}
- Redis的并发
在日常开发中,有时会遇到这样的场景:多个人同时对同一个数据进行修改,导致并发问题发生。使用Redis来解决这个问题是很好的选择。
Redis管道使得客户端能够用“无等待响应”的方式,来连续发送多条命令请求至Redis服务器端,然后服务器端按照请求顺序返回相应的结果。类似于如下形式:
client> set key1 value1;
client> set key2 value2;
client> set key3 value3;
server> ok
server> ok
server> ok
Redis管道(Pipelining)的操作可以理解为并发操作,并通过Send()、Flush()、Receive()这3个方法实现。客户端可以用Send()方法一次性向服务器发送一个或多个命令。命令发送完毕后,用Flush()方法将缓冲区的命令一次性发送到服务器端,客户端再用Receive()方法依次按照先进先
出的顺序读取所有命令的结果。Redis并发的示例如下。
import (
"fmt"
"github.com/gomodule/redigo/redis"
)
func main() {
c, err := redis.Dial("tcp", "localhost:6379",
redis.DialPassword("123456"),
redis.DialDatabase(0))
if err != nil {
fmt.Println(err)
return
}
defer c.Close()
c.Send("HSet", "students", "name", "jim", "age", "19")
c.Send("HSet", "students", "score", "100")
c.Send("HGET", "students", "age")
c.Flush()
res1, err := c.Receive()
fmt.Printf("Receive res1:%v\n", res1)
res2, err := c.Receive()
fmt.Printf("Receive res2:%v\n", res2)
res3, err := c.Receive()
fmt.Printf("Receive res3:%s\n", res3)
}
- Redis的事务
MULTI、EXEC、DISCARD和NATCH方法是构成Redis事务的基础。使用Go语言对Redis进行事务操作的本质也是使用这些命令。
- MULTI:开启事务;
- EXEC:执行事务;
- DISCARD:取消事务;
- WATCH:监视事务中的键变化,一旦有改变则取消事务;
/*
事务
*/
func main() {
c, err := redis.Dial("tcp", "localhost:6379",
redis.DialPassword("123456"),
redis.DialDatabase(0))
if err != nil {
fmt.Println(err)
return
}
defer c.Close()
c.Send("MULTI")
c.Send("WATCH", "foo")
c.Send("INCR", "foo")
c.Send("INCR", "bar")
r, err := c.Do("EXEC")
fmt.Println(r)
}