Skip to content

Redis ​

阅读提示

  • 适用版本:Go 1.21+(示例在 Go 1.22 下验证)· 前置知识:标准库-strings · 阅读时长:约 12 分钟

Redis 是一个开源的使用 ANSI C 语言编写、遵守 BSD 协议、支持网络、可基于内存、分布式、可选持久性的键值对(Key-Value)存储数据库,并提供多种语言的 API,Redis 即可以当作一个 NoSQL 数据库,又可以是当作高速缓存存储,还支持简单的消息队列。

本文仅仅讲解如何使用 Go 语言驱动来操作 Redis 数据库,不会对 Redis 本身做任何讲解。

官方文档:Golang Redis client (uptrace.dev)

官方仓库:go-redis/redis: Type-safe Redis client for Golang (github.com)

安装 ​

关于 Redis 的驱动有很多,本文使用的是github.com/go-redis/redis。

如果你使用的 Redis 版本号为 6

go get github.com/go-redis/redis/v8

如果你使用的 Redis 版本号为 7

go get github.com/go-redis/redis/v9

快速开始 ​

go
import (
   "fmt"
   "log"
   "testing"

   "github.com/go-redis/redis"
)

func TestQuickStart(t *testing.T) {
   // 创建Redis连接客户端
   redisClient := redis.NewClient(&redis.Options{
      Addr:     "192.168.48.134:6379",
      Password: "123456",
      DB:       0, // 使用默认DB
   })

   // 设置键值对,0就是永不过期
   redisClient.Set("hello", "world", 0)

   // 读取值
   result, err := redisClient.Get("hello").Result()
   if err == redis.Nil {
      fmt.Println("ket not exist")
   } else if err != nil {
      log.Panic(err)
   }
   fmt.Println(result)
}

连接配置 ​

go
type Options struct {
  // 网络类型 tcp 或者 unix.
  // 默认是 tcp.
  Network string
  // redis地址,格式 host:port
  Addr string

    // Dialer 创建一个新的网络连接且比Network和Addr有着更高的优先级
  // Network and Addr options.
  Dialer func() (net.Conn, error)

  // 新建一个redis连接的时候,会回调这个函数
  OnConnect func(*Conn) error

  // redis密码,redis server没有设置可以为空。
  Password string

  // redis数据库,序号从0开始,默认是0,可以不用设置
  DB int

  // redis操作失败最大重试次数,默认0。
  MaxRetries int

  // 最小重试时间间隔.
  // 默认是 8ms ; -1 表示关闭.
  MinRetryBackoff time.Duration

  // 最大重试时间间隔
  // 默认是 512ms; -1 表示关闭.
  MaxRetryBackoff time.Duration

  // redis新连接超时时间.
  // 默认是 5 秒.
  DialTimeout time.Duration

  // socket读取超时时间
  // 默认 3 秒.
  ReadTimeout time.Duration

  // socket写超时时间
  WriteTimeout time.Duration

  // redis连接池的最大连接数.
  // 默认连接池大小等于 cpu个数 * 10
  PoolSize int

  // redis连接池最小空闲连接数.
  MinIdleConns int

  // redis连接最大的存活时间,默认不会关闭过时的连接.
  MaxConnAge time.Duration

  // 当你从redis连接池获取一个连接之后,连接池最多等待这个拿出去的连接多长时间。
  // 默认是等待 ReadTimeout + 1 秒.
  PoolTimeout time.Duration

  // redis连接池多久会关闭一个空闲连接.
  // 默认是 5 分钟. -1 则表示关闭这个配置项
  IdleTimeout time.Duration

  // 多长时间检测一下,空闲连接
  // 默认是 1 分钟. -1 表示关闭空闲连接检测
  IdleCheckFrequency time.Duration

  // 只读设置,如果设置为true, 在当前节点实例上,redis只能查询缓存不能更新。
  readOnly bool

    // TLS配置
  TLSConfig *tls.Config
}

建立连接 ​

go
// 创建Redis连接客户端
redisClient := redis.NewClient(&redis.Options{
    Addr:     "192.168.48.134:6379",
    Password: "123456",
    DB:       0, // 使用默认DB
})

关闭连接 ​

驱动内部维护着一个连接池,不需要操作一次就关闭一次连接。

go
defer redisClient.Close()

这个 Redis 驱动几乎将所有的操作封装好了,Redis 命令和方法名一一对应,基本上只要知道 Redis 命令怎么用,驱动对应的方法都也差不多会了。

Redis 命令:redis 命令手册

基本操作 ​

删除键 ​

go
redisClient.Set("name", "jack", 0)
fmt.Println(redisClient.Del("name").Result())

过期时间 ​

go
redisClient.Set("name", "jack", 0)
// 设置过期时间
redisClient.Expire("name", time.Second*2)
fmt.Println(redisClient.Get("name").Val())
time.Sleep(time.Second * 3)
fmt.Println(redisClient.Get("name").Val())

取消过期时间 ​

go
redisClient.Set("name", "jack", 2)
// 取消过期时间
redisClient.Persist("name")
time.Sleep(time.Second * 2)
fmt.Println(redisClient.Get("name"))

查询过期时间 ​

go
fmt.Println(redisClient.TTL("name"))
fmt.Println(redisClient.PTTL("name"))

重命名 ​

go
redisClient.Rename("name", "newName")

查询类型 ​

go
redisClient.Type("name")

扫描 ​

go
fmt.Println(redisClient.Scan(0, "", 4))

字符串 ​

简单存取 ​

go
redisClient.Set("token", "abcefghijklmn", 0)
fmt.Println(redisClient.Get("token").Val())

批量存取 ​

go
redisClient.MSet("cookie", "12345", "token", "abcefg")
fmt.Println(redisClient.MGet("cookie", "token").Val())

数字增减 ​

go
redisClient.Set("age", "1", 0)
// 自增
redisClient.Incr("age")
fmt.Println(redisClient.Get("age").Val())
// 自减
redisClient.Decr("age")
fmt.Println(redisClient.Get("age").Val())

哈希表 ​

读写操作 ​

go
// 单个设置
redisClient.HSet("map", "name", "jack")
// 批量设置
redisClient.HMSet("map", map[string]interface{}{"a": "b", "c": "d", "e": "f"})
// 单个访问
fmt.Println(redisClient.HGet("map", "a").Val())
// 批量访问
fmt.Println(redisClient.HMGet("map", "a", "b").Val())
// 获取整个map
fmt.Println(redisClient.HGetAll("map").Val())

输出

b
[b <nil>]
map[a:b c:d e:f name:jack]

删除键 ​

go
// 删除map的一个字段
redisClient.HDel("map", "a")

判断键是否存在 ​

go
// 判断字段是否存在
redisClient.HExists("map", "a")

获取所有的键 ​

go
// 获取所有的map的键
redisClient.HKeys("map")

获取哈希表键长度 ​

go
// 获取map长度
redisClient.HLen("map")

遍历哈希表的键值对 ​

go
// 遍历map中的键值对
redisClient.HScan("map", 0, "", 1)

列表 ​

修改元素 ​

go
// 左边添加
redisClient.LPush("list", "a", "b", "c", "d", "e")
// 右边添加
redisClient.RPush("list", "g", "i", "a")
// 在参考值前面插入值
redisClient.LInsertBefore("list", "a", "aa")
// 在参考值后面插入值
redisClient.LInsertAfter("list", "a", "gg")
// 设置指定下标的元素的值
redisClient.LSet("list", 0, "head")

访问长度 ​

go
// 访问列表长度
redisClient.LLen("list")

访问元素 ​

go
// 左边弹出元素
redisClient.LPop("list")
// 右边弹出元素
redisClient.RPop("list")
// 访问指定下标的元素
redisClient.LIndex("list", 1)
// 访问指定范围内的元素
redisClient.LRange("list", 0, 1)

删除元素 ​

go
// 删除指定元素
redisClient.LRem("list", 0, "a")
// 删除指定范围的元素
redisClient.LTrim("list", 0, 1)
// 保留指定范围的元素
redisClient.LTrim("list", 0, 1)

集合 ​

新增元素 ​

go
// 往一个集合里面添加元素
redisClient.SAdd("set", "a", "b", "c")
redisClient.SAdd("set2", "c", "d", "e")

访问集合元素 ​

go
// 获取集合中的所有成员
redisClient.SMembers("set")
// 判断一个元素是否属于这个集合
redisClient.SIsMember("set", "a")
// 随机返回count个元素
redisClient.SRandMemberN("set", 1)
// 获取一个集合的元素个数
redisClient.SCard("set")

集合操作 ​

go
// 返回给定集合的差集
redisClient.SDiff("set", "set2")
// 将给定集合的差集保存在结果集里,返回结果集的长度
redisClient.SDiffStore("store", "set", "se2")
// 返回给定集合的交集
redisClient.SInter("set", "set2")
// 将给定集合的交集保存在结果集里,返回结果集的长度
redisClient.SInterStore("store", "set", "set2")
// 返回给定集合的并集
redisClient.SUnion("set", "set2")
// 将给定集合的并集保存在结果集里,返回结果集的长度
redisClient.SUnionStore("store", "set", "store")

删除元素 ​

go
// 弹出并删除该元素
redisClient.SPop("set")
// 弹出并删除N给元素
redisClient.SPopN("set", 2)

移动元素 ​

go
// 从源集合移动指定元素刀目标集合
redisClient.SMove("set", "set2", "a")

删除元素 ​

go
// 删除指定元素
redisClient.SRem("set", "a", "b")

遍历 ​

go
// 遍历集合
redisClient.SScan("set", 0, "", 2)

有序集合 ​

加入元素 ​

go
// 往有序集合中加入元素
redisClient.ZAdd("ss", redis.Z{
   Score:  1,
   Member: "a",
}, redis.Z{
   Score:  2,
   Member: "b",
})

元素排名 ​

go
// 返回有序集合中该元素的排名,从低到高排列
redisClient.ZRank("ss", "1")
// 返回有序集合中该元素的排名,从高到低排列
redisClient.ZRevRank("ss", "1")

访问元素 ​

go
// 返回介于min和max之间的成员数量
redisClient.ZCount("ss", "1", "2")

// 返回对元素的权值
redisClient.ZScore("ss", "a")

// 返回指定区间的元素
redisClient.ZRange("ss", 1, 2)
// 返回介于min和max之间的所有成员列表
redisClient.ZRangeByScore("ss", redis.ZRangeBy{
   Min:    "1",
   Max:    "2",
   Offset: 0,
   Count:  1,
})

修改权值 ​

go
// 给一个对应的元素增加相应的权值
redisClient.ZIncr("ss", redis.Z{
   Score:  2,
   Member: "b",
})

删除元素 ​

go
// 删除指定元素
redisClient.ZRem("ss", "a")
// 删除指定排名区间的元素
redisClient.ZRemRangeByRank("ss", 1, 2)
// 删除权值在min和max区间的元素
redisClient.ZRemRangeByScore("ss", "1", "2")

脚本 ​

go
// 加载脚本,返回sha值
redisClient.ScriptLoad("return 0")
// 根据sha值执行脚本
redisClient.EvalSha("sha", []string{}, "")
// 直接执行脚本
redisClient.Eval("return 0", []string{}, "")
// 清除脚本缓存
redisClient.ScriptFlush()
// 杀死当前正在运行的脚本
redisClient.ScriptKill()
// 验证对应哈希值的脚本是否存在
redisClient.ScriptExists("")

发布订阅 ​

go
// 发送消息到指定频道
redisClient.Publish("channel", "message")
// 订阅指定频道
redisClient.Subscribe("channel")
// 查看订阅状态
redisClient.PubSubNumSub("channel")

完整示例 ​

下面的程序可以直接运行:复制到文件后执行 go run main.go,或粘贴到 Go Playground 运行。

go
package main

import (
	"fmt"
	"strings"
)

func main() {
	args := []string{"SET", "user:1", "gopher"}

	// Redis 客户端使用 RESP 协议:先发 *N,再依次发送 bulk string
	var sb strings.Builder
	fmt.Fprintf(&sb, "*%d\r\n", len(args))
	for _, a := range args {
		fmt.Fprintf(&sb, "$%d\r\n%s\r\n", len(a), a)
	}

	fmt.Printf("%q\n", sb.String())
}

预期输出:

text
"*3\r\n$3\r\nSET\r\n$6\r\nuser:1\r\n$6\r\ngopher\r\n"

常见错误与踩坑 ​

  • 客户端是并发安全的。 go-redis 可以被多个协程共享;通常整个进程只持有一个 client。
  • 一定要设 TTL。 没有过期时间的 key 会一直堆积,直到实例内存耗尽。
  • 警惕大 key。 单次巨型 HGETALL、KEYS 会阻塞服务端,应改为 SCAN 与拆分。
  • 批量操作使用 Pipeline。 延迟主要来自往返次数;Pipeline 能显著减少 RTT。
  • Pub/Sub 不保证投递。 消息不持久化;需要可靠投递时应使用 Streams。

更新记录 ​

  • 2026-10-14:新增"阅读提示"、完整可运行示例(含预期输出)、常见错误与踩坑清单,以及本更新记录。