go-redis 开发手册:用 Go 语言玩转 Redis 的正确方法
Redis 是一种流行的内存键值数据库,用于构建缓存和高性能消息队列应用程序。本文将介绍如何通过go-redis访问redis。
go-redis简介
go-redis是Go语言中非常流行的Redis客户端库。与其他Go语言Redis客户端相比,它具有以下优点:
- API友好,命令名称和参数与Redis原生命令一致,使用简单方便。
- 支持完整的Redis命令集,包括字符串、哈希、列表、集合、有序集、HyperLogLog等数据结构。扩大。
- 支持Pipelines和事务,并且可以打包多个命令以减少网络开销。
- 支持Pub/Sub通知和键盘等功能。
- 支持哨兵和集群模式,提供高可用性。
- 代码正在积极维护中,并且不断添加新功能。
- 在 Github 上拥有 15,000 多个星星,它是最受欢迎的 Go Redis 客户端。
简而言之,go-redis 是一个功能强大且易于使用的 Redis 客户端库。它包含了Redis的大部分功能,是Go语言连接Redis的首选解决方案。
go-redis使用演示
package mainimport ("fmt""github.com/go-redis/redis")// 创建redis客户端func newClient() *redis.Client {client := redis.NewClient(&redis.Options{Addr: "localhost:6379", // redis地址Password: "", // 密码DB: 0, // 使用默认数据库})return client}func main() {// 创建客户端client := newClient()defer client.Close()// 设置keyerr := client.Set("name", "john", 0).Err()if err != nil {panic(err)}// 获取keyval, err := client.Get("name").Result()if err != nil {panic(err)}fmt.Println("name", val)}
如代码所示,go-redis提供了非常简单的API来连接Redis服务器并执行命令
go-redis常用功能package main
import ( "fmt"
"github.com/go-redis/redis")
func subscriber(client *redis.Client) { pubsub := client.Subscribe("mychannel") defer pubsub.Close()
// 处理订阅接收到的消息 for { msg, err := pubsub.ReceiveMessage() if err != nil { return }
fmt.Println(msg.Channel, msg.Payload) }}
func publisher(client *redis.Client) { for { // 发布消息到频道 err := client.Publish("mychannel", "hello").Err() if err != nil { panic(err) } }}
func main() { client := redis.NewClient(&redis.Options{ Addr: "localhost:6379", })
go subscriber(client) go publisher(client)
<-make(chan struct{})}
- 通过client .Subscribe 订阅频道,然后循环接收消息。
- 打开另一个goroutine来发布消息。使用go-redis轻松实现发布订阅模型。
消息队列
package main
import ( "fmt" "math/rand" "time"
"github.com/go-redis/redis")
var client *redis.Client
// 初始化连接func initClient() { client = redis.NewClient(&redis.Options{ Addr: "localhost:6379", Password: "", DB: 0, })}
// 生产者 - 发布消息func producer() { for { message := rand.Intn(1000) err := client.LPush("queue", message).Err() if err != nil { panic(err) } fmt.Println("pushed", message) time.Sleep(1 * time.Second) }}
// 消费者 - 处理消息func consumer(id int) { for { message, err := client.BRPop(0, "queue").Result() if err != nil { panic(err) } fmt.Printf("consumer%d popped %s \n", id, message[1]) time.Sleep(500 * time.Millisecond) }}
func main() { // 初始化 initClient()
// 生产者goroutine go producer()
// 3个消费者goroutine for i := 0; i < 3; i++ { go consumer(i) }
// 阻塞主goroutine <-make(chan struct{}) }
- 使用BRPop实现块出队和LPush加入队列,可以基于Redis创建消息队列。
- 或许消费者可以共享队列来实现负载均衡
管道访问
package main
import ( "fmt"
"github.com/go-redis/redis")
func subscriber(client *redis.Client) { pubsub := client.Subscribe("mychannel") defer pubsub.Close()
// 处理订阅接收到的消息 for { msg, err := pubsub.ReceiveMessage() if err != nil { return }
fmt.Println(msg.Channel, msg.Payload) }}
func publisher(client *redis.Client) { for { // 发布消息到频道 err := client.Publish("mychannel", "hello").Err() if err != nil { panic(err) } }}
func main() { client := redis.NewClient(&redis.Options{ Addr: "localhost:6379", })
go subscriber(client) go publisher(client)
<-make(chan struct{})}package main
import ( "fmt" "math/rand" "time"
"github.com/go-redis/redis")
var client *redis.Client
// 初始化连接func initClient() { client = redis.NewClient(&redis.Options{ Addr: "localhost:6379", Password: "", DB: 0, })}
// 生产者 - 发布消息func producer() { for { message := rand.Intn(1000) err := client.LPush("queue", message).Err() if err != nil { panic(err) } fmt.Println("pushed", message) time.Sleep(1 * time.Second) }}
// 消费者 - 处理消息func consumer(id int) { for { message, err := client.BRPop(0, "queue").Result() if err != nil { panic(err) } fmt.Printf("consumer%d popped %s \n", id, message[1]) time.Sleep(500 * time.Millisecond) }}
func main() { // 初始化 initClient()
// 生产者goroutine go producer()
// 3个消费者goroutine for i := 0; i < 3; i++ { go consumer(i) }
// 阻塞主goroutine <-make(chan struct{}) }Redis Pipeline实现批量调度请求和响应模式,允许客户端在单个网络交互命令中缓冲和发送多个请求,然后接收所有请求再次一起回应。这种方法大大减少了客户端和服务器之间的网络往返次数,优化了网络传输开销,显着提高了Redis的整体吞吐量和命令处理性能。测试结果表明Pipeline模式可以使Redis命令处理TPS的吞吐量提高数倍。同时,也减少了客户端等待响应的时间,有效隐藏了网络通信延迟。
另外,较大的数据包可以通过Pipeline模式轻松传输,避免小数据包命令多次网络传输的资源消耗。它还减少了客户端和服务器端的 CPU 和内存使用量。
总之,Redis Pipeline通过优化网络传输、批量命令执行等方式来提升Redis性能,是优化客户端访问的一个非常重要的方式。特别适合高负载、低延迟的Redis访问场景。
func pipelineDemo(client *redis.Client) {// 创建pipelinepipe := client.Pipeline()// 添加多个命令到pipelinesetCmd := pipe.Set("key1", "value1", 0)getCmd := pipe.Get("key1")incrCmd := pipe.Incr("counter")pipe.Expire("key1", time.Hour)// 执行pipeline_, err := pipe.Exec()if err != nil {panic(err)}fmt.Println("setCmd:", setCmd.Val())fmt.Println("getCmd:", getCmd.Val())fmt.Println("incrCmd:", incrCmd.Val())}
Pipeline是通过redis.Pipeline()创建的,使用pipeline对象添加命令,最后调用Exec()执行。
- 返回的Cmd包含每个命令的响应,可以按顺序处理。
- 这样可以批量发送多条命令,优化对Redis服务器的访问。
访问Redis集群
func clusterDemo() {// 集群节点,可以填一个或者多个// go-redis通过部分节点获取整个集群拓扑(所有节点信息)client := redis.NewClusterClient(&redis.ClusterOptions{Addrs: []string{"127.0.0.1:7000","127.0.0.1:7001","127.0.0.1:7002",},})err := client.Set("key", "value", 0).Err()if err != nil {panic(err)}val, err := client.Get("key").Result()if err != nil {panic(err)}fmt.Println("key", val)err = client.Close()if err != nil {panic(err)}}
- 通过NewClusterClient创建客户端,输入集群节点地址。
- 然后您就可以像使用独立客户端一样立即操作集群。
- go-redis 会自动将请求路由到正确的节点。
- 所以go-redis可以非常方便的访问Redis集群
Transaction(事务)
func transactionDemo(client *redis.Client) {pipe := client.TxPipeline()pipe.Set("key1", "value1", 0)pipe.Set("key2", "value2", 0)_, err := pipe.Exec()if err != nil {panic(err)}}
- 使用TxPipeline()创建事务管道。
- 然后将命令添加到管道中,最后调用 Exec() 来执行。
- 这会将所有命令作为事务发送到 Redis 服务器。
需要注意的是,Redis 中的事务不是原子操作。它只是一种将各种命令打包然后依次执行的机制。与关系数据库中的事务不同,如果Redis事务中某个命令执行失败,则不会执行下一个命令,但整个事务不会回滚。
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
code前端网
