package main
import (
"context"
"fmt"
"github.com/go-redis/redis/v8"
)
func main() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password set
DB: 0, // use default DB
})
fmt.Println(rdb)
// Redis
// 执行命令
err := rdb.Set(ctx, "key", "value", 0).Err()
if err != nil {
panic(err)
}
val, err := rdb.Get(ctx, "key").Result()
if err != nil {
panic(err)
}
fmt.Println("key", val) // key value
val2, err := rdb.Get(ctx, "key2").Result()
if err == redis.Nil {
fmt.Println("key2 does not exist")
} else if err != nil {
panic(err)
} else {
fmt.Println("key2", val2)
//key2 does not exist
}
}
1.操作String类型
操作redis String类型
String 类型是 Redis 中最基本的数据类型,string 类型的值最大能存储 512MB。
下面是go-redis操作String字符串类型的一些API:
- Set:设置字符串的值
- SetEX:设置并指定过期时间(set expire)
- SetNX:设置并指定过期时间(if not exists) 如果key已经存在,则返回err
- Get:获取字符串的值
- GetRange:获取字符串值的子字符
这些API的演示代码如下:
package main
import (
"context"
"fmt"
"time"
"github.com/go-redis/redis/v8"
)
func main() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "",
DB: 0,
})
_, err := rdb.Ping(ctx).Result() // PING,
if err != nil {
fmt.Println("connect redis failed:", err)
return
}
// Set 设置
err = rdb.Set(ctx, "key", "thisisavalue", 0).Err() // 0表示永不过期
if err != nil {
panic(err)
}
err = rdb.Set(ctx, "key2", "this is a value2", time.Minute*2).Err() // key2将会在两分钟后过期失效
if err != nil {
panic(err)
}
fmt.Println("set success")
// SetEX 设置并指定过期时间
err = rdb.SetEX(ctx, "key3", "this is a value3", time.Hour*2).Err()
if err != nil {
panic(err)
}
fmt.Println("SetEX success")
// SetNX 设置并指定过期时间(如果key已经存在返回err
_, err = rdb.SetNX(ctx, "key4", "this is a value4", time.Minute).Result()
if err != nil {
fmt.Println("SetNX failed, key4已经存在", err)
}
fmt.Println("SetNX success")
// Get 获取
result, err := rdb.Get(ctx, "key").Result()
if err != nil {
panic(err)
}
fmt.Println("key值:", result)
result, err = rdb.Get(ctx, "key5").Result()
// redis.Nil来区分空字符串应答和Nil应答(键不存在)
if err == redis.Nil {
fmt.Println("key5不存在")
} else if err != nil {
panic(err)
} else {
fmt.Println("key5值为:", result)
}
// GetRange 获取字符串值的子字符
result, err = rdb.GetRange(ctx, "key", 1, 4).Result()
if err != nil {
panic(err)
}
fmt.Printf("GetRange截取key值为: %vn", result)
}
// 打印结果如下:
下面是增减量的API:
- Incr:将 key 中储存的数字值增加1
- IncrBy:将 key 所储存的值加上给定的增量值
- Decr:将 key 中储存的数字值减1
- DecrBy:key 所储存的值减去给定的减量值
这些API的演示代码如下:
package main
import (
"context"
"fmt"
"github.com/go-redis/redis/v8"
)
func main() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "",
DB: 0,
})
_, err := rdb.Ping(ctx).Result() // PING,
if err != nil {
fmt.Println("connect redis failed:", err)
return
}
// Incr 增加+1
result, err := rdb.Incr(ctx, "count").Result()
if err != nil {
panic(err)
}
fmt.Printf("count值为: %vn", result)
// IncrBy 按指定步长增加
result, err = rdb.IncrBy(ctx, "count", 2).Result()
if err != nil {
panic(err)
}
fmt.Printf("count值为: %vn", result)
// Decr 减少-1
result, err = rdb.Decr(ctx, "count").Result()
if err != nil {
panic(err)
}
fmt.Printf("count值为: %vn", result)
// DecrBy 按指定步长减少
result, err = rdb.DecrBy(ctx, "count", 2).Result()
if err != nil {
panic(err)
}
fmt.Printf("count值为: %vn", result)
}
// 打印结果如下:
2.操作List类型
Redis 列表是简单的字符串列表,按照插入顺序排序。可以添加一个元素到列表的头部(左边)或者尾部(右边)。
下面是go-redis操作redis List列表类型的一些API:
- LPush:在列表的左侧加入数据
- LLen:获取链表元素个数
- LIndex:获取列表某下标对应元素
- LInsert:向某个索引位置插入一个元素
- LSet:设置列表某个索引位置的值
- Lpop:从列表左侧弹出数据
- LRange:获取某个索引范围里的元素
- LTrim:对一个列表进行修剪,只让列表保留指定区间内的元素,不在指定区间之内的元素都将被删除
package main
import (
"context"
"fmt"
"github.com/go-redis/redis/v8"
)
func main() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "",
DB: 0,
})
_, err := rdb.Ping(ctx).Result() // PING,
if err != nil {
fmt.Println("connect redis failed:", err)
return
}
result, err := rdb.Exists(ctx, "test_list").Result()
if err != nil {
return
}
if result == 1{
fmt.Println("test_list have existed ")
value, err := rdb.Del(ctx,"test_list").Result()
if err != nil {
return
}
if value ==1 {
fmt.Println("test_list delete success!")
}
}
// LPush(ctx, key, values...) 在列表的左侧加入数据
n, err := rdb.LPush(ctx, "test_list", 1, 2, 3, 4, 5, 6).Result()
if err != nil {
panic(err)
}
fmt.Println(n)
// LLen(ctx, key) 获取链表元素个数
len, err := rdb.LLen(ctx, "test_list").Result()
if err != nil {
panic(err)
}
fmt.Println("test_list的长度:", len)
// LIndex(ctx, key, index) 获取列表某下标对应元素
val, err := rdb.LIndex(ctx, "test_list", 2).Result() // 索引从0数起。
if err != nil {
panic(err)
}
// 6 5 4 3 2 1
fmt.Println("index是2的值:", val)
// LInsert(ctx, key, op, pivot, value) 向某个索引位置插入一个元素
err = rdb.LInsert(ctx, "test_list", "before", "2", 8).Err() // 在test_list列表的2的前面插入8。
if err != nil {
panic(err)
}
// 6 5 4 3 8 2 1
fmt.Println("插入成功")
// LSet(ctx, key, index, value) 设置列表某个索引位置的值
err = rdb.LSet(ctx, "test_list", 1, 9).Err() // 给索引为1 的位置设置为9。索引从0数起。
if err != nil {
panic(err)
}
fmt.Println("设置成功")
fmt.Println(rdb.LRange(ctx,"test_list",0,-1).Result())
// 6 9 4 3 8 2 1
// LPop(ctx, key) 从列表左侧弹出数据
val, err = rdb.LPop(ctx, "test_list").Result()
if err != nil {
panic(err)
}
fmt.Println("从列表左侧弹出元素:", val)
// 9 4 3 8 2 1
// LRange(ctx, key, start, stop) 获取某个索引范围里的元素
vals, err := rdb.LRange(ctx, "test_list", 1, 4).Result()
if err != nil {
panic(err)
}
fmt.Println("获取索引1到4的值:", vals)
// LTrim(ctx, key, start, stop) 对一个列表进行修剪,
// 只让列表保留指定区间内的元素,不在指定区间之内的元素都将被删除
val, err = rdb.LTrim(ctx, "test_list", 1, 4).Result() // 此时只剩下1到4范围内的值
if err != nil {
panic(err)
}
fmt.Println("val值:", val)
}
// 打印结果如下:
3.操作redis Hash类型
Redis hash 是一个键值(key=>value)对集合。Redis hash 是一个 string 类型的 field 和 value 的映射表,hash 特别适合用于存储对象。
下面是go-redis操作redis Hash类型的一些API:
- HSet:给key设置field和value
- HGet:获取key某个field的value
- HMset:给key批量设置field和value
- HLen:获取键值对个数
- HGetAll:获取全部键值对元素
- HKeys:获取所有的key
- HIncrBy:增加,成功返回增加后的值
- HDel:删除键值对元素
- HExists:检查key某个field是否存在
package main
import (
"context"
"fmt"
"github.com/go-redis/redis/v8"
)
func main() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "",
DB: 0,
})
_, err := rdb.Ping(ctx).Result() // PING,
if err != nil {
fmt.Println("connect redis failed:", err)
return
}
// HSet 给key设置field和value
result, err := rdb.HSet(ctx, "user1", "name", "zhangsan").Result()
if err != nil {
panic(err)
}
fmt.Println(result) // 1
// HMSet 批量设置
rdb.HMSet(ctx, "user1", map[string]interface{}{"age": 18, "sex": "male"})
// HGet 获取某个field的value
name, err := rdb.HGet(ctx, "user1", "name").Result()
if err != nil {
panic(err)
}
fmt.Println(name) // zhangsan
// HMGet 批量获取field的value
fields, err := rdb.HMGet(ctx, "user1", "name", "age").Result()
if err != nil {
panic(err)
}
fmt.Println(fields) // [zhangsan 18]
// HLen 获取键值对个数
len, err := rdb.HLen(ctx, "user1").Result()
if err != nil {
panic(err)
}
fmt.Println(len) // 3
// HGetAll 获取全部元素
all, err := rdb.HGetAll(ctx, "user1").Result()
if err != nil {
panic(err)
}
fmt.Println(all) // map[age:18 name:zhangsan sex:male]
// HKeys 获取所有的key
keys, err := rdb.HKeys(ctx, "user1").Result()
if err != nil {
panic(err)
}
fmt.Println(keys) // [name age sex]
// HIncrBy 增加,成功返回增加后的值
age, err := rdb.HIncrBy(ctx, "user1", "age", 2).Result()
if err != nil {
panic(err)
}
fmt.Println(age) // 20
// HDel 删除键值对元素
result, err = rdb.HDel(ctx, "user1", "name").Result()
if err != nil {
panic(err)
}
fmt.Println(result) // 1
// HExists 检查某个元素是否存在
fmt.Println(rdb.HExists(ctx, "user1","name")) // hexists user1 name: false
}
// 打印结果如下:
4.操作redis Set类型
Redis 的 Set 是 string 类型的无序集合。
集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。
下面是go-redis操作redis Set集合类型的一些API:
- SAdd:向指定集合添加集合元素
- SSMembers:获取集合中的所有成员
- SCard:获取集合中成员的数量
- SIsMember:判断某成员是否在集合中
- SRem:删除集合里指定成员
- SPop:随机从集合中弹出一个元素
package main
import (
"context"
"fmt"
"github.com/go-redis/redis/v8"
)
func main() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "",
DB: 0,
})
_, err := rdb.Ping(ctx).Result() // PING,
if err != nil {
fmt.Println("connect redis failed:", err)
return
}
// SAdd 向指定集合添加集合元素
result, err := rdb.SAdd(ctx, "dev_users", "zhangsan", "lisi", "wangwu", "zhangsan").Result()
if err != nil {
panic(err)
}
fmt.Println(result) // 3
// SMembers 获取集合中的所有成员
members, err := rdb.SMembers(ctx, "dev_users").Result()
if err != nil {
panic(err)
}
fmt.Println(members) // [zhangsan wangwu lisi]
// SCard 获取集合中成员的数量
count, err := rdb.SCard(ctx, "dev_users").Result()
if err != nil {
panic(err)
}
fmt.Println(count) // 3
// SIsMember 判断某成员是否在集合中
is, err := rdb.SIsMember(ctx, "dev_users", "zhaoliu").Result()
if err != nil {
panic(err)
}
fmt.Println(is) // false
// SRem 删除集合里指定成员
n, err := rdb.SRem(ctx, "dev_users", "zhangsan", "lisi").Result()
if err != nil {
panic(err)
}
fmt.Println(n) // 2
// SPop 随机从集合中弹出一个元素
user, err := rdb.SPop(ctx, "dev_users").Result() // 此时集合中只剩wangwu,弹出wangwu了
if err != nil {
panic(err)
}
fmt.Println(user) // wangwu
}
// 打印结果如下:
下面是go-redis集合运算的一些API:
- SInter:求集合间的交集
- SDiff:求集合间的差集
- SUnion:求集合间的并集
package main
import (
"context"
"fmt"
"github.com/go-redis/redis/v8"
)
func main() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "",
DB: 0,
})
_, err := rdb.Ping(ctx).Result() // PING,
if err != nil {
fmt.Println("connect redis failed:", err)
return
}
// 准备一些数据
// 先往dev_group1集合添加一些成员
result1, err := rdb.SAdd(ctx, "dev_group1", "zhangsan", "lisi", "wangwu", "zhangsan").Result()
if err != nil {
panic(err)
}
fmt.Println(result1) // 3
// 再往dev_group2集合添加一些成员
result2, err := rdb.SAdd(ctx, "dev_group2", "zhaoliu", "tiaiqi", "zhangsan", "lisi").Result()
if err != nil {
panic(err)
}
fmt.Println(result2) // 4
// SInter 求集合间的交集
inter, err := rdb.SInter(ctx, "dev_group1", "dev_group2").Result()
if err != nil {
panic(err)
}
fmt.Println("交集:", inter) // 交集: [zhangsan lisi]
// SDiff 求集合间的差集
diff, err := rdb.SDiff(ctx, "dev_group1", "dev_group2").Result() // dev_group1集合与dev_group2集合的差集
if err != nil {
panic(err)
}
fmt.Println("差集:", diff) // 差集: [wangwu]
diff2, err := rdb.SDiff(ctx, "dev_group2", "dev_group1").Result() // dev_group2集合与dev_group1集合的差集
if err != nil {
panic(err)
}
fmt.Println("差集:", diff2) // 差集: [zhaoliu tiaiqi]
// SUnion 求集合间的并集
union, err := rdb.SUnion(ctx, "dev_group1", "dev_group2").Result()
if err != nil {
panic(err)
}
fmt.Println("并集:", union) // 并集: [lisi wangwu zhaoliu tiaiqi zhangsan]
}
// 打印结果如下:
5.操作redis Zset类型
Redis zset 和 set 一样也是string类型元素的集合,且不允许重复的成员。不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。zset的成员是唯一的,但分数(score)却可以重复。
下面是go-redis操作redis Zset有序集合类型的一些API:
- ZAdd:向一个有序集合中添加元素
- ZCard:获取有序集合的元素个数
- ZCount:获取有序集合指定区间内元素个数
- ZScore:获取有序集合的元素的score
- ZRank:返回有序集中指定成员的排名。其中有序集成员按分数值递增(从小到大)顺序排列。
- ZRevRank:返回有序集合中指定成员的排名,有序集成员按分数值递减(从大到小)排序
- ZRange:返回有序集中,指定区间内的成员,其中成员的位置按分数值递增(从小到大)来排序。
- ZRevRange:返回有序集中,指定区间内的成员,其中成员的位置按分数值递减(从大到小)来排列。
- ZIncrBy:对有序集合中指定成员的分数加上一个增量
- ZRangeByScore:通过分数返回有序集合指定区间内的成员,分数从低到高排序
- ZRevRangeByScore:返回有序集中指定分数区间内的成员,分数从高到低排序
- ZRemRangeByRank:移除有序集合中给定的排名区间的所有成员
- ZRemRangeByScore:移除有序集合中给定的分数区间的所有成员
- ZRem:移除有序集合中的一个或多个成员
package main
import (
"context"
"fmt"
"github.com/go-redis/redis/v8"
)
func main() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "",
DB: 0,
})
_, err := rdb.Ping(ctx).Result() // PING,
if err != nil {
fmt.Println("connect redis failed:", err)
return
}
// ZAdd 向一个有序集合中添加元素
rdb.ZAdd(ctx, "test_zset", &redis.Z{
Score: 1,
Member: "python",
})
rdb.ZAdd(ctx, "test_zset", &redis.Z{
Score: 2,
Member: "c",
})
rdb.ZAdd(ctx, "test_zset", &redis.Z{
Score: 3,
Member: "java",
})
rdb.ZAdd(ctx, "test_zset", &redis.Z{
Score: 4,
Member: "c++",
})
rdb.ZAdd(ctx, "test_zset", &redis.Z{
Score: 5,
Member: "c#",
})
rdb.ZAdd(ctx, "test_zset", &redis.Z{
Score: 6,
Member: "visual bacis",
})
// ZCard 获取有序集合的元素个数
count, err := rdb.ZCard(ctx, "test_zset").Result()
if err != nil {
panic(err)
}
fmt.Println(count) // 6
// ZCount 获取有序集合指定区间内元素个数
// fmt.Println(rdb.ZCount(ctx, "test_zset", "2", "4"))
n, err := rdb.ZCount(ctx, "test_zset", "2", "4").Result()
if err != nil {
panic(err)
}
fmt.Println(n) // 3
// ZScore 获取有序集合的元素的score
score, err := rdb.ZScore(ctx, "test_zset", "c++").Result()
if err != nil {
panic(err)
}
fmt.Println(score) // 4
// ZRank 返回有序集中指定成员的排名。其中有序集成员按分数值递增(从小到大)顺序排列。
result, err := rdb.ZRank(ctx, "test_zset", "java").Result()
if err != nil {
panic(err)
}
fmt.Println(result) // 2
// ZRevRank 返回有序集合中指定成员的排名,有序集成员按分数值递减(从大到小)排序
result, err = rdb.ZRevRank(ctx, "test_zset", "java").Result()
if err != nil {
panic(err)
}
fmt.Println(result) // 3
// ZRange 返回有序集中,指定区间内的成员,其中成员的位置按分数值递增(从小到大)来排序。
elems, err := rdb.ZRange(ctx, "test_zset", 0, 2).Result()
if err != nil {
panic(err)
}
fmt.Println(elems) // [python c java]
// ZRevRange 返回有序集中,指定区间内的成员,其中成员的位置按分数值递减(从大到小)来排列。
elems, err = rdb.ZRevRange(ctx, "test_zset", 0, 2).Result()
if err != nil {
panic(err)
}
fmt.Println(elems) // [visual bacis c# c++]
// ZIncrBy 对有序集合中指定成员的分数加上一个增量
r, err := rdb.ZIncrBy(ctx, "test_zset", 2, "c++").Result()
if err != nil {
panic(err)
}
fmt.Println(r) // 6
// ZRangeByScore 通过分数返回有序集合指定区间内的成员,分数从低到高排序
res, err := rdb.ZRangeByScore(ctx, "test_zset", &redis.ZRangeBy{
Min: "2",
Max: "5",
}).Result()
if err != nil {
panic(err)
}
fmt.Println(res) // [c java c#]
// ZRevRangeByScore 返回有序集中指定分数区间内的成员,分数从高到低排序
res, err = rdb.ZRevRangeByScore(ctx, "test_zset", &redis.ZRangeBy{
Min: "2",
Max: "5",
}).Result()
if err != nil {
panic(err)
}
fmt.Println(res) // [c# java c]
// ZRem 移除有序集合中的一个或多个成员
rem, err := rdb.ZRem(ctx, "test_zset", "visual bacis").Result()
if err != nil {
panic(err)
}
// 删除的元素个数
fmt.Println(rem) // 1
// ZRemRangeByRank 移除有序集合中给定的排名区间的所有成员
// 按照升序删除第一个和第二个元素
rem, err = rdb.ZRemRangeByRank(ctx, "test_zset", 0, 1).Result()
if err != nil {
panic(err)
}
fmt.Println(rem) // 2
// ZRemRangeByScore 移除有序集合中给定的分数区间的所有成员
// 删除score在2-4之间的元素
rem, err = rdb.ZRemRangeByScore(ctx, "test_zset", "2", "4").Result()
if err != nil {
panic(err)
}
fmt.Println(rem) // 1
}
// 打印结果如下:
6.管道操作
Redis管道可以在服务端未响应时,客户端继续向服务端发送请求,并最终一次性读取所有服务端的响应。管道技术最显著的优势是提高了 redis 服务的性能。
下面是管道的操作示例:
package main
import (
"context"
"fmt"
"time"
"github.com/go-redis/redis/v8"
)
func main() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "",
DB: 0,
})
_, err := rdb.Ping(ctx).Result() // PING,
if err != nil {
fmt.Println("connect redis failed:", err)
return
}
var incr *redis.IntCmd
_, err = rdb.Pipelined(ctx, func(piper redis.Pipeliner) error {
incr = piper.Incr(ctx, "pipelined_counter")
piper.Expire(ctx, "pipelined_counter", time.Hour)
return nil
})
if err != nil {
panic(err)
}
fmt.Println(incr.Val()) // 1
}
// 打印结果如下:
7.事务操作
事务可以理解为一个打包的批量执行脚本,但批量指令并非原子化的操作,中间某条指令的失败不会导致前面已做指令的回滚,也不会造成后续的指令不做。
一个事务从开始到执行会经历三个阶段:开始事务、命令入队及执行事务。
这里是事务操作的演示:
待填。
8.发布/订阅
Redis发布订阅是一种消息通信模式:发送者 (pub) 发送消息,订阅者 (sub) 接收消息。Redis的客户端可订阅任意数量的频道。
Redis发布的演示代码:
// pub.go
package main
import (
"context"
"fmt"
"github.com/go-redis/redis/v8"
)
func main() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "",
DB: 0,
})
_, err := rdb.Ping(ctx).Result() // PING,
if err != nil {
fmt.Println("connect redis failed:", err)
return
}
// 发布
err := rdb.Publish(ctx, "mychannel1", "payload").Err()
if err != nil {
panic(err)
}
fmt.Println("publish done!")
}
Redis订阅的演示代码:
// sub.go
package main
import (
"context"
"fmt"
"github.com/go-redis/redis/v8"
)
func main() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "",
DB: 0,
})
_, err := rdb.Ping(ctx).Result() // PING,
if err != nil {
fmt.Println("connect redis failed:", err)
return
}
// 订阅频道
pubsub := rdb.Subscribe(ctx, "mychannel1")
// 完成后关闭订阅。
defer pubsub.Close()
// 接收消息
for {
msg, err := pubsub.ReceiveMessage(ctx)
if err != nil {
panic(err)
}
fmt.Println(msg.Channel, msg.Payload)
}
}
先启动订阅代码sub.go,再启动发布代码pub.go,即可看到效果。
参考文章
传送门



