Redis
是我们开发应用程序中很常用的NoSQL
数据库,作为一款内存数据库,Redis
也支持数据持久化(RDB
与AOF
两种机制),所以Redis
既可以作为数据库来使用,也可以作为关系型数据库与应用程序之间的缓存来使用。
那么在Go语言中要如何连接和操作Redis
呢?在这篇文章中,我们一起来探究一下!
开始
在开始操作Redis
之前,我们先来初始化我们的示例项目,接着再安装连接Redis
所需要的第三方库。
初始化项目
由于我们接下来要用到的第三方库需要Go Moudles
的支持,因此我们先使用go mod init
命令初始化我们的示例项目:
go mod init GoTest
Go Redis
由于Go语言的标准库并没有提供对Redis
的支持,因此就需要使用到Go
社区的第三方库。
在这里推荐用Go Reids
,该库最新版本的Github
地址为:
github.com/redis/go-redis
为什么推荐使用Go Redis
推荐使用Go Redis
的原因有以下几点:
Go Redis
是Redis
官方推荐的第三方库。- 社区中使用的人也比较多,而且文档很齐全。
- 支持不同种类的
Redis
客户端,比如哨兵客户端,集群客户端等。 - 支持所有的
Redis
版本。
Go Redis安装
通过cd
命令进入项目,执行go get
命令就可以下载安装 github.com/redis/go-redis/v9
:
cd GoTest #最新版本为v9 go get github.com/redis/go-redis/v9
连接Redis
如同所有的Client/Server
应用程序一样,要想操作Redis
,就必须先与Redis
建立网络连接。
简单连接
如果是连接部署在内部网络没有密码的Redis
服务器,只需要IP
与端口号(port)
就可以连接了:
import "github.com/redis/go-redis/v9" opt := &redis.Options{ Addr: "localhost:6379", } rdb := redis.NewClient(opt)
上面代码中,可以概述为以下两个步骤:
- 实例化一个
redis.Options
表示连接配置对象,该对象当前设置了Addr
字段,该 字段表示Redis
服务器的地址。 - 将
redis.Options
对象传给redis.NewClient()
函数,redis.NewClient()
函数会返回一个操作Redis
的句柄rdb
。
redis.ParseURL
除了自定义redis.Options
,另一种方式是调用redis.ParseURL()
函数以下格式的URL
,该函数会返回一个redis.Options
对象:
redis://<user>:<pass>@localhost:6379/<port>
因此我们可以将上面连接Redis
的例子改写为:
import "github.com/redis/go-redis/v9" opt, err := redis.ParseURL("redis://localhost:6379") if err != nil { panic(err) } rdb := redis.NewClient(opt)
redis.Options
前面我们连接Redis
的例子中只用到了redis.Options
的Addr
字段:
opt := &redis.Options{ Addr: "localhost:6379", }
除此之外,我们也可以通过redis.Options
的字段配置连接的参数:
opt := &redis.Options{ //地址与端口 Addr: "localhost:6379", //数据库,0~16 DB:0, //用户名,在Redis6.0以上使用 Username:"test", //密码 Password:"123456", //命令最大重试次数 MaxRetries:3, //连接超时 DialTimeout:3, //连接池 PoolSize:30, //最小空闲连接数 MinIdleConns:10, //最大空闲连接数 MaxIdleConns:30, }
上面列举的是redis.Options
比较常用的字段,实际上还有很多其他的字段参数,这里就不一一列举了。
Redis操作
虽然已经通过redis.NewClient()
函数获取操作Redis
的句柄了,但要操作Redis
了,还需要创建一个Context
对象,这是因为所有的Go Redis
操作Redis
的方法的第一个参数都是Context
。
创建Context
Context
表示上下文,可以用于超时控制、数据传递以及性能监控等。
通过context
包的Backgroud()
函数可以创建一个根Context
:
ctx := context.Background()
执行已支持的命令
Go Redis
为所有的Redis
命令都提供了对应的方法,比如SET
命令对应的方法为Set
,HGET
对应的方法为HGet
。
因此如果想执行对应的Redis
命令,直接调用对应的方法即可,比如:
redis> set test test_value 0 redis> hset user:1 id 1 name 小张 gender 男
使用Go Redis
执行上面命令的代码为:
rdb.Set(ctx, "test", "test_value", 0) rdb.HSet(ctx, "user:1", "id",1,"name", "小张", "gender", "男")
Redis
命令执行后,一般都有返回值,但是不同的命令的返回值是不一样的,最简单的返回如SET
命令返回一个字符串,HSET
命令会返回一个整数,而HGETALL
会返回一个类似结构体的数据:
package main import ( "context" "fmt" "github.com/redis/go-redis/v9" ) func main() { rdb := redis.NewClient(&redis.Options{ Addr: "localhost:6379", }) ctx := context.Background() setCmd := rdb.Set(ctx, "test", "test_value", 0) hSetCmd := rdb.HSet(ctx, "user:1", "id", 1, "name", "小张", "gender", "男") hAllGetCmd := rdb.HGetAll(ctx, "user:1") }
在上面的代码中,我们可以看到在执行不同命令时,会返回不同cmd
对象,比如Set()
方法返回一个StatusCmd
对象,HSet()
方法返回IntCmd
对象,而HGetAll()
方法返回MapStringStringCmd
对象。
有了cmd
对象后,就可以获取命令执行结果及错误信息,有两种方法,一种是直接调用Result()
方法返回结果与错误信息:
fmt.Println(setCmd.Result()) fmt.Println(hSetCmd.Result()) fmt.Println(hAllGetCmd.Result())
另一种方法是分开获得取结果和错误信息:
fmt.Println(setCmd.Val(),setCmd.Err()) fmt.Println(hSetCmd.Val(),hSetCmd.Err()) fmt.Println(hAllGetCmd.Val(),hAllGetCmd.Err())
上面两种输出方式输出的的结果都是:
OK <nil>
3 <nil>
map[gender:男 id:1 name:小张] <nil>
如果我们获得取一个不存在的key,此时命令会返回一个特殊的错误redis.Nil
,这是一个特殊的错误,用于表示是否获得取了空值:
val, err := rdb.Get(ctx, "key").Result() switch { case err == redis.Nil: fmt.Println("key不存在") case err != nil: fmt.Println("错误", err) case val == "": fmt.Println("值是空字符串") }
执行尚未支持的命令
当然,有时候Redis
新的版本可能会添加一些新的命令,而Go Redis
还未提供支持,这时候可以调用Do
方法来执行对应的命令,实际上Do()
方法可以用于执行任何命令:
val, err := rdb.Do(ctx, "hgetall", "user:1").Result() if err != nil { if err == redis.Nil { fmt.Println("key does not exists") return } panic(err) } fmt.Println(val.(MapStringStringCmd))
从上面的例子可以看出Do()
执行不同的命令后返回值也是不同的。
结果集映射
有时候我们查询Redis后会返回多个key-value
的结果集,比如mget
、hget
,hgetall
这样的命令,Go redis
提供了Scan()
方法可以将查询回来的结果集扫描进对应的结构体当中:
package main import ( "context" "fmt" "github.com/redis/go-redis/v9" ) type User struct { ID int `redis:"id"` Name string `redis:"name"` Gender string `redis:"gender"` } func main() { rdb := redis.NewClient(&redis.Options{ Addr: "localhost:6379", }) ctx := context.Background() var u User err := rdb.HGetAll(ctx, "user:1").Scan(&u) if err != nil { panic(err) } fmt.Println(u) }
管道
管道(pipeline)
允许在一次请求中发送多条Redis
命令,并返回多个结果,这样可以节省一个一个执行命令需要付出的往返回时间:
package main import ( "context" "fmt" "github.com/redis/go-redis/v9" ) func main() { opt := &redis.Options{ Addr: "localhost:6379", } rdb := redis.NewClient(opt) ctx := context.Background() cmds, err := rdb.Pipelined(ctx, func(pipe redis.Pipeliner) error { pipe.Set(ctx, "test", "test_value", 0) pipe.HSet(ctx, "user:1", "id", 1, "name", "小张", "gender", "男") pipe.HGetAll(ctx, "user:1") return nil }) if err != nil { panic(err) } for _, cmd := range cmds { switch c := cmd.(type) { case *redis.IntCmd: fmt.Println(c.Val()) case *redis.StatusCmd: fmt.Println(c.Val()) case *redis.MapStringStringCmd: fmt.Println(c.Val()) } } }
发布与订阅
Go Redis
支持发布与订阅(Pub/Sub
)。
发布消息
发布消息的方法为Publish
:
package main import ( "context" "fmt" "github.com/redis/go-redis/v9" ) func main() { rdb := redis.NewClient(&redis.Options{ Addr: "localhost:6379", }) ctx := context.Background() //向渠道发送消息 err := rdb.Publish(ctx, "mychannel", "this is a message").Err() if err != nil { panic(err) } }
订阅消息
订阅消息的方法为Subscribe
,订阅之后通过返回的句柄调用ReceiveMessage()
方法接收消息:
package main import ( "context" "fmt" "github.com/redis/go-redis/v9" ) func main() { rdb := redis.NewClient(&redis.Options{ Addr: "localhost:6379", }) ctx := context.Background() pubsub := rdb.Subscribe(ctx, "mychannel") defer pubsub.Close() //接收消息 for { msg, err := pubsub.ReceiveMessage(ctx) if err != nil { panic(err) } fmt.Printf("从%s渠道接受到%s", msg.Channel, msg.Payload) } }
小结
在本文中,我们讲解用github.com/redis/go-redis
连接和操作Redis的基本操作,其实这个库支持很多高级的功能,比如哨兵,集群、分片等功能,以后在别的文章再讲解吧。
以上就是重学Go语言之如何使用Redis的详细内容,更多关于Go Redis的资料请关注好代码网其它相关文章!