准备
安装第三方开源Redis库
go mod init redis-demo
go get github.com/redis/go-redis/v9
然后我们先不管别的 ,我们来一段代码,来试试看看可不可以跑通,我们再来继续学习
package main
import (
"context"
"fmt"
"github.com/redis/go-redis/v9" // 导入驱动
)
// Redis 的所有操作都需要一个“背景上下文”,用于管理超时或追踪
var ctx = context.Background()
func main() {
// 1. 配置连接参数
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379", // Redis 运行地址
Password: "", // 如果没有设置密码则为空
DB: 0, // 默认数据库索引(0-15)
})
// 2. 检查连接状态 (Ping-Pong 机制)
pong, err := rdb.Ping(ctx).Result()
if err != nil {
fmt.Println("连接 Redis 失败:", err)
return
}
fmt.Println("连接成功:", pong) // 成功会输出 PONG
}
解释:
redis.Newclient根据Options创建一个Redis客户端对象,初始化连接池等等组件。redis.Options是go-redis库里面定义的一个结构体,用于配置Redis客户端连接参数。

我们成功连接到了数据库,我们开始后续的学习。
如果发现,能够连接到数据库,但是有报错提示

在设置中启用Go模块集成就好了。
String的操作
注意事项
1.context使用时,控制超时
var ctx = context.Background()
go-redis的所有操作(Set, Get, Ping)都强制要求第一个参数传ctx。 它的作用是:如果你想让这个操作在 5 秒后如果没有结果就自动取消(超时控制),你就需要通过ctx来告诉 Redis。context.Background()是 Go 中上下文机制的“起点”,它本身不做任何事,但可以派生出有超时、可取消、带值的子上下文,主要用于主程序、服务启动、或作为其他上下文的父级。
2.错误处理,特别是怎么判断“key不存在”
代码案例
/*链接数据库*/
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
DB: 0,
})
/*写入*/
err := rdb.Set(ctx, "my_key", "Hello Go", 0).Err()
if err != nil {
panic(err)
}
fmt.Println("successful")
/*读取*/
_, err2 := rdb.Get(ctx, "not_exists").Result()
if err2 == redis.Nil {
fmt.Println("This key does not exist")
}
/*分布式锁*/
isSuccess, err := rdb.SetNX(ctx, "lock_key", "locked", 10*time.Second).Result()
if err != nil {
panic(err)
}
if isSuccess {
fmt.Println("Locked successfully")
} else {
fmt.Println("Locked Unsuccessfully")
}
关键解析:
1.Set(ctx, string, interface{}, time.Duration)
ctx: 上下文(必填)。key: 你的键名(字符串)。value: 你的值。注意这里是interface{},意味着你可以传 string, int, bool, bytes,Go 会尝试自动帮你转。expiration: 过期时间。- 传
0:表示永不过期(直到你手动删除或内存满了)。 - 传
10 * time.Second:Go 语言的时间单位。表示 10 秒后 Redis 自动删除它。
- 传
2..Err()和.Result()
.Err():我不关心返回值,我只关心成没成功。.Result():我要拿到具体的返回值。
3.redis.Nil
这是一个特定的错误厂里,如果不判断这个,程序可能吧没找到数据单程数据库挂了来处理。
4.rdb.SetNX(ctx, key, value, expiration)
这是一个分布锁,通过返回值判断抢没抢到锁
5.panic(err)
一旦发生错误,程序立刻停止,打印出错误,但是实际上班开发中,应该Println出日志,或者返回错误给前端,不能让服务器直接寄。
Hash操作
代码案例
/*链接数据库*/
rdb1 := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
DB: 1,
})
key := "user:profile:1001"
/*存入一整个对象,这里可以直接传一个map进去*/
userMap := map[string]interface{}{
"name": "Ttdr",
"age": 21,
"base": "NanJing",
"money": 0,
}
err1 := rdb1.HSet(ctx, key, userMap).Err()
if err1 != nil {
panic(err1)
}
fmt.Println("Hash successful")
/*获取一个字段*/
_, err = rdb1.HGet(ctx, key, "name").Result()
if err != nil {
panic(err)
}
fmt.Println("name get successfully")
/*字段增减*/
rdb.HIncrBy(ctx, key, "age", 1)
rdb.HIncrBy(ctx, key, "money", 100000000)
fmt.Println("age and money have changed")
/*获得全部*/
allInfo, err := rdb.HGetAll(ctx, key).Result()
if err != nil {
panic(err)
}
for field, val := range allInfo {
fmt.Printf("%s:%s\n", field, val)
}
关键分析
1.rdb.HSet(ctx, key, values)
Go 语言里,value最优雅的写法是传入 map[string]interface{}
这里因为是空接口,可以接受任何类型的数据
2.HGetAll
返回值类型:map[string]string
Redis 的 Hash 底层本质上存的 value 都是字符串,即使你存进去的是int,返回的也是字符串,所以不能用这个返回值直接加减。
3.HIncrBy
它只锁定 Hash 里的某一个小格子进行修改,是原子性的,不影响其他格子,和Incr一样,具有并发安全。
补充对结构体的Hash
type User struct {
Name string `redis:"name"` // Redis 里字段名叫 name
Age int `redis:"age"` // Redis 里字段名叫 age
Email string `redis:"email"` // Redis 里字段名叫 email
}
u1 := User{
Name: "极客兔兔",
Age: 22,
Email: "go@example.com",
}
err := rdb.HSet(ctx, key, &u1).Err()
if err != nil {
panic(err)
}
fmt.Println("Struct 保存成功!")
var u2 User
err = rdb.HGetAll(ctx, key).Scan(&u2)
if err != nil {
panic(err)
}
不同的点是在于读取的时候用Scan填回一个新结构体,这里会把字符串的整数还原成int类型的整数
Redis连接池
每一次程序和Redi建立连接,都会进行TCP的三次握手,也会导致一个问题,在高并发情况下,所有CPU都在忙着握手回收,真正干活的很少,Redis就是来简化这个过程,避免了三次握手导致的资源浪费。
redis.NewClient返回的rdb对象本身就是一个线程安全(可以在多个 Goroutine 里随便用,不需要加锁。)的连接池,我们也就不需要其他操作。
我们就来从代码层面深入了解一下,怎么配置一个线程池。
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
DB: 0,
PoolSize: 10,
MinIdleConns: 2,
PoolTimeout: 30 * time.Second,
MaxRetries: 3,
})
var wg sync.WaitGroup
start := time.Now()
for i := 0; i < 50; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
err := rdb.Set(ctx, fmt.Sprintf("key:%d", id), id, 0).Err()
if err != nil {
fmt.Println(id, "协程", err)
}
}(i)
}
wg.Wait()
fmt.Println("all ok time:", time.Since(start))
fmt.Println("最终连接池状态:", rdb.PoolStats())
1.配置字段
PoolSize(最大连接数)MinIdleConns(最小空闲连接,保证来了请求就能用)PoolTimeout(等待超时)MaxRetries(重试次数)
2.监控器func (c *Client) PoolStats() *PoolStats
返回结构体为:
{
"Hits": 50,
"Misses": 6,
"Timeouts": 0,
"TotalConns": 42,
"IdleConns": 0,
"StaleConns": 0
}










