# go-redis文档
# 1 Redis优势
更多Redis学习请参考本网站Redis教程
Redis 优势
- 性能极高 –-
Redis
能读的速度是110000次/s
,写的速度是81000次/s
。 - 丰富的数据类型 –-
Redis
支持二进制案例的Strings,Lists,Hashes,Sets及Ordered Sets
数据类型操作。 - 原子--
Redis
的所有操作都是原子性的,同时Redis
还支持对几个操作全并后的原子性执行。 - 丰富的特性 – Redis还支持
publish/subscribe
,通知,key
过期等等特性。
# 2 安装
Redis
的安装很简单,我这里测试直接用的是windows
的版本。如何windows安装请自行百度。linux安装Redis教程
# 2.1 安装依赖包
golang
客户端,用的是go-redis
,直接使用go get包下载下来导入即可。go-redis文档
go get -u github.com/go-redis/redis
# 3 Go连接Redis
go-redis
包自带了连接池,会自动维护redis
连接,因此创建一次client
即可,不要查询一次redis
就关闭client
。
package main
import (
"fmt"
"github.com/go-redis/redis"
)
// 声明一个全局的redisDb变量
var redisDb *redis.Client
// 根据redis配置初始化一个客户端
func initClient() (err error) {
redisDb = redis.NewClient(&redis.Options{
Addr: "localhost:6379", // redis地址
Password: "", // redis密码,没有则留空
DB: 0, // 默认数据库,默认是0
})
//通过 *redis.Client.Ping() 来检查是否成功连接到了redis服务器
_, err = redisDb.Ping().Result()
if err != nil {
return err
}
return nil
}
func main() {
err := initClient()
if err != nil {
//redis连接错误
panic(err)
}
fmt.Println("Redis连接成功")
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 3.1 redis.Options参数详解
type Options struct {
// 网络类型 tcp 或者 unix.
// 默认是 tcp.
Network string
// redis地址,格式 host:port
Addr string
// 新建一个redis连接的时候,会回调这个函数
OnConnect func(*Conn) error
// redis密码,redis server没有设置可以为空。
Password string
// redis数据库,序号从0开始,默认是0,可以不用设置
DB int
// redis操作失败最大重试次数,默认不重试。
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
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# 4 基本键值操作
redis
基本的key/value
操作,指的是针对value
值的类型为字符串或者数字类型的读写操作。go-redis
常用函数如下:
type Cmdable interface {
//给数据库中名称为key的string赋予值value,并设置失效时间,0为永久有效
Set(key string, value interface{}, expiration time.Duration) *StatusCmd
//查询数据库中名称为key的value值
Get(key string) *StringCmd
//设置一个key的值,并返回这个key的旧值
GetSet(key string, value interface{}) *StringCmd
//如果key不存在,则设置这个key的值,并设置key的失效时间。如果key存在,则设置不生效
SetNX(key string, value interface{}, expiration time.Duration) *BoolCmd
//批量查询key的值。比如redisDb.MGet("name1","name2","name3")
MGet(keys ...string) *SliceCmd
//批量设置key的值。redisDb.MSet("key1", "value1", "key2", "value2", "key3", "value3")
MSet(pairs ...interface{}) *StatusCmd
//Incr函数每次加一,key对应的值必须是整数或nil
//否则会报错incr key1: ERR value is not an integer or out of range
Incr(key string) *IntCmd
// IncrBy函数,可以指定每次递增多少,key对应的值必须是整数或nil
IncrBy(key string, value int64) *IntCmd
// IncrByFloat函数,可以指定每次递增多少,跟IncrBy的区别是累加的是浮点数
IncrByFloat(key string, value float64) *FloatCmd
// Decr函数每次减一,key对应的值必须是整数或nil.否则会报错
Decr(key string) *IntCmd
//DecrBy,可以指定每次递减多少,key对应的值必须是整数或nil
DecrBy(key string, decrement int64) *IntCmd
//删除key操作,支持批量删除 redisDb.Del("key1","key2","key3")
Del(keys ...string) *IntCmd
//设置key的过期时间,单位秒
Expire(key string, expiration time.Duration) *BoolCmd
//给数据库中名称为key的string值追加value
Append(key, value string) *IntCmd
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 4.1 Set & Get示例
func main() {
err := initClient()
if err != nil {
//redis连接错误
panic(err)
}
// 第三个参数代表key的过期时间,0代表不会过期。
err = redisDb.Set("name1", "zhangsan", 0).Err()
if err != nil {
panic(err)
}
var val string
// Result函数返回两个值,第一个是key的值,第二个是错误信息
val, err = redisDb.Get("name1").Result()
// 判断查询是否出错
if err != nil {
panic(err)
}
fmt.Println("name1的值:", val) //name1的值:zhangsan
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 4.2 GetSet & SetNX示例
func main() {
err := initClient()
if err != nil {
//redis连接错误
panic(err)
}
// 第三个参数代表key的过期时间,0代表不会过期。
err = redisDb.Set("name1", "zhangsan", 0).Err()
if err != nil {
panic(err)
}
var oldVal string
// Result函数返回两个值,第一个是key的值,第二个是错误信息
oldVal, err = redisDb.GetSet("name1", "new_zhangsan").Result()
if err != nil {
panic(err)
}
// 打印key的旧值
fmt.Println("name1:", oldVal)
//如果key不存在,则设置这个key的值,并设置key的失效时间。如果key存在,则设置不生效
err = redisDb.SetNX("name2", "lisi", 0).Err()
if err != nil {
panic(err)
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 4.3 MGet & MSet示例
func main() {
err := initClient()
if err != nil {
//redis连接错误
panic(err)
}
//批量设置key1对应的值为value1,key2对应的值为value2,key3对应的值为value3,
err = redisDb.MSet("key1", "value1", "key2", "value2", "key3", "value3").Err()
if err != nil {
panic(err)
}
// MGet函数可以传入任意个key,一次性返回多个值。
// 这里Result返回两个值,第一个值是一个数组,第二个值是错误信息
vals, err := redisDb.MGet("key1", "key2", "key3").Result()
if err != nil {
panic(err)
}
fmt.Println(vals) //[value1 value2 value3]
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 4.4 自增自减操作
func main() {
err := initClient()
if err != nil {
//redis连接错误
panic(err)
}
//设置一个age测试自增、自减
err = redisDb.Set("age", "20", 0).Err()
if err != nil {
panic(err)
}
redisDb.Incr("age") // 自增
redisDb.IncrBy("age", 5) //+5
redisDb.Decr("age") // 自减
redisDb.DecrBy("age", 3) //-3 此时age的值是22
var val string
val, err= redisDb.Get("age").Result()
fmt.Println("age=",val) //22
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 4.5 Del & Expire & Append示例
func main() {
err := initClient()
if err != nil {
//redis连接错误
panic(err)
}
//设置一个name对应的值是hello
err = redisDb.Set("name", "hello", 0).Err()
if err != nil {
panic(err)
}
redisDb.Append("name", "China")
val,err:=redisDb.Get("name").Result()
fmt.Println(val) //helloChina
//设置key的过期时间为5秒
redisDb.Expire("name", 5)
//批量删除
redisDb.Del("name1", "name2", "key1", "key2")
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 5 list操作
Redis
列表(list
)是简单的字符串列表,列表是有序的,列表中的元素可以重复.可以添加一个元素到列表的头部(左边)或者尾部(右边)常用操作方法如下:
type Cmdable interface {
//从列表左边插入数据,list不存在则新建一个继续插入数据
LPush(key string, values ...interface{}) *IntCmd
//跟LPush的区别是,仅当列表存在的时候才插入数据
LPushX(key string, value interface{}) *IntCmd
//返回名称为 key 的 list 中 start 至 end 之间的元素
//返回从0开始到-1位置之间的数据,意思就是返回全部数据
LRange(key string, start, stop int64) *StringSliceCmd
//返回列表的长度大小
LLen(key string) *IntCmd
//截取名称为key的list的数据,list的数据为截取后的值
LTrim(key string, start, stop int64) *StatusCmd
//根据索引坐标,查询列表中的数据
LIndex(key string, index int64) *StringCmd
//给名称为key的list中index位置的元素赋值
LSet(key string, index int64, value interface{}) *StatusCmd
//在指定位置插入数据。op为"after或者before"
LInsert(key, op string, pivot, value interface{}) *IntCmd
//在指定位置前面插入数据
LInsertBefore(key string, pivot, value interface{}) *IntCmd
//在指定位置后面插入数据
LInsertAfter(key string, pivot, value interface{}) *IntCmd
//从列表左边删除第一个数据,并返回删除的数据
LPop(key string) *StringCmd
//删除列表中的数据。删除count个key的list中值为value 的元素。
LRem(key string, count int64, value interface{}) *IntCmd
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 5.1 LPush & LPushX 示例
func main() {
err := initClient()
if err != nil {
//redis连接错误
panic(err)
}
//仅当列表存在的时候才插入数据,此时列表不存在,无法插入
redisDb.LPushX("studentList", "tom")
//此时列表不存在,依然可以插入
redisDb.LPush("studentList", "jack")
//此时列表存在的时候才能插入数据
redisDb.LPushX("studentList", "tom")
// LPush支持一次插入任意个数据
err = redisDb.LPush("studentList", "lily","lilei","zhangsan","lisi").Err()
if err != nil {
panic(err)
}
// 返回从0开始到-1位置之间的数据,意思就是返回全部数据
vals, err := redisDb.LRange("studentList",0,-1).Result()
if err != nil {
panic(err)
}
fmt.Println(vals) //注意列表是有序的,输出结果是[lisi zhangsan lilei lily tom jack]
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 5.2 LRange & LLen示例
func main() {
err := initClient()
if err != nil {
//redis连接错误
panic(err)
}
// LPush支持一次插入任意个数据
err = redisDb.LPush("studentList", "lily","lilei","zhangsan","lisi").Err()
if err != nil {
panic(err)
}
// 返回从[0,2]位置之间的数据,意思就是返回3个数据
vals, err := redisDb.LRange("studentList",0,2).Result()
if err != nil {
panic(err)
}
fmt.Println(vals) //注意列表是有序的,输出结果是[lisi zhangsan lilei]
// 返回从0开始到-1位置之间的数据,意思就是返回全部数据
vals, err = redisDb.LRange("studentList",0,-1).Result()
if err != nil {
panic(err)
}
//返回list集合中的长度
studentLen, err :=redisDb.LLen("studentList").Result()
if err != nil {
panic(err)
}
fmt.Println("student集合的长度为:",studentLen)
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 5.3 LTrim & LIndex 示例
func main() {
err := initClient()
if err != nil {
//redis连接错误
panic(err)
}
// LPush支持一次插入任意个数据
err = redisDb.LPush("studentList", "lily", "lilei", "zhangsan", "lisi", "tom").Err()
if err != nil {
panic(err)
}
// 返回从0开始到-1位置之间的数据,意思就是返回全部数据
vals, err := redisDb.LRange("studentList",0,-1).Result()
if err != nil {
panic(err)
}
fmt.Println(vals) //注意列表是有序的,输出结果是[tom lisi zhangsan lilei lily]
// 列表索引从0开始计算,这里返回第3个元素
index, err := redisDb.LIndex("studentList", 2).Result()
if err != nil {
panic(err)
}
fmt.Println(index) //zhangsan
//截取名称为key的list,并把截取后的值赋值给studentList
val:= redisDb.LTrim("studentList", 0, 3)
fmt.Println(val) //ltrim studentList 0 3: OK
// 返回从0开始到-1位置之间的数据,意思就是返回全部数据
vals, err= redisDb.LRange("studentList",0,-1).Result()
if err != nil {
panic(err)
}
fmt.Println(vals) //[tom lisi zhangsan lilei]
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# 5.4 LSet & LInsert示例
func main() {
err := initClient()
if err != nil {
//redis连接错误
panic(err)
}
// LPush支持一次插入任意个数据
err = redisDb.LPush("studentList", "lily", "lilei", "zhangsan", "lisi", "tom").Err()
if err != nil {
panic(err)
}
//给名称为key的list中index位置的元素赋值,把原来的数据覆盖
redisDb.LSet("studentList", 2, "beer")
// 返回从0开始到-1位置之间的数据,意思就是返回全部数据
vals, err := redisDb.LRange("studentList", 0, -1).Result()
if err != nil {
panic(err)
}
fmt.Println(vals) //注意列表是[tom lisi beer lilei lily]
//在list列表studentList中值为lilei前面添加元素hello
redisDb.LInsert("studentList", "before", "lilei", "hello")
//redisDb.LInsertBefore("studentList","lilei","hello") 执行效果同22行
//在list列表studentList中值为tom后面添加元素world
redisDb.LInsert("studentList", "after", "tom", "world")
//redisDb.LInsertAfter("studentList","tom","world") 执行效果同26行
// 返回从0开始到-1位置之间的数据,意思就是返回全部数据
vals, err = redisDb.LRange("studentList", 0, -1).Result()
if err != nil {
panic(err)
}
fmt.Println(vals) //[tom world lisi beer hello lilei lily]
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# 5.5 LPop & LRem示例
func main() {
err := initClient()
if err != nil {
//redis连接错误
panic(err)
}
// LPush支持一次插入任意个数据
err = redisDb.LPush("studentList", "lily", "lilei", "zhangsan", "lisi", "tom", "lisi", "laowang").Err()
if err != nil {
panic(err)
}
vals, err := redisDb.LRange("studentList", 0, -1).Result()
if err != nil {
panic(err)
}
fmt.Println("还未进行任何删除时列表中的值为:", vals) //[laowang lisi tom lisi zhangsan lilei lily]
//从列表左边删除第一个数据,并返回删除的数据
redisDb.LPop("studentList")
// 返回从0开始到-1位置之间的数据,意思就是返回全部数据
vals, err = redisDb.LRange("studentList", 0, -1).Result()
if err != nil {
panic(err)
}
fmt.Println("进行了LPop删除操作后列表的值:", vals) //[lisi tom lisi zhangsan lilei lily]
//删除列表中的数据。删除count个key的list中值为value 的元素。如果出现重复元素,仅删除1次,也就是删除第一个
redisDb.LRem("studentList", 10, "lisi")
vals, err = redisDb.LRange("studentList", 0, -1).Result()
if err != nil {
panic(err)
}
fmt.Println("进行了LRem删除操作后列表的值:", vals) // [tom zhangsan lilei lily]
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 6 集合set操作
redi
s的set类型
(集合)是string
类型数值的无序集合,并且集合元素唯一。常用函数如下:
type Cmdable interface {
//向名称为key的set中添加元素member
SAdd(key string, members ...interface{}) *IntCmd
//获取集合set元素个数
SCard(key string) *IntCmd
//判断元素member是否在集合set中
SIsMember(key string, member interface{}) *BoolCmd
//返回名称为 key 的 set 的所有元素
SMembers(key string) *StringSliceCmd
//求差集
SDiff(keys ...string) *StringSliceCmd
//求差集并将差集保存到 destination 的集合
SDiffStore(destination string, keys ...string) *IntCmd
//求交集
SInter(keys ...string) *StringSliceCmd
//求交集并将交集保存到 destination 的集合
SInterStore(destination string, keys ...string) *IntCmd
//求并集
SUnion(keys ...string) *StringSliceCmd
//求并集并将并集保存到 destination 的集合
SUnionStore(destination string, keys ...string) *IntCmd
//随机返回集合中的一个元素,并且删除这个元素
SPop(key string) *StringCmd
// 随机返回集合中的count个元素,并且删除这些元素
SPopN(key string, count int64) *StringSliceCmd
//删除名称为 key 的 set 中的元素 member,并返回删除的元素个数
SRem(key string, members ...interface{}) *IntCmd
//随机返回名称为 key 的 set 的一个元素
SRandMember(key string) *StringCmd
//随机返回名称为 key 的 set 的count个元素
SRandMemberN(key string, count int64) *StringSliceCmd
//把集合里的元素转换成map的key
SMembersMap(key string) *StringStructMapCmd
//移动集合source中的一个member元素到集合destination中去
SMove(source, destination string, member interface{}) *BoolCmd
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# 6.1 SAdd & SCard示例
func main() {
err := initClient()
if err != nil {
//redis连接错误
panic(err)
}
// 添加100到集合中
err = redisDb.SAdd("stuSet", 100).Err()
if err != nil {
panic(err)
}
// 将100,200,300批量添加到集合中 集合的元素不能重复
redisDb.SAdd("stuSet", 100, 200, 300)
//获取集合中元素的个数
size, err := redisDb.SCard("stuSet").Result()
if err != nil {
panic(err)
}
fmt.Println(size) //3
//返回名称为集合中的所有元素
es, err := redisDb.SMembers("stuSet").Result()
fmt.Println(es) //[100 200 300]
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 6.2 SIsMember & SMembers示例
func main() {
err := initClient()
if err != nil {
//redis连接错误
panic(err)
}
// 添加100到集合中
err = redisDb.SAdd("stuSet", 100).Err()
if err != nil {
panic(err)
}
// 将100,200,300批量添加到集合中 集合的元素不能重复
redisDb.SAdd("stuSet", 100, 200, 300)
//返回名称为集合中的所有元素
es, err := redisDb.SMembers("stuSet").Result()
fmt.Println(es) //[100 200 300]
//此处flag1=true
flag1, err := redisDb.SIsMember("stuSet", "200").Result()
if flag1 {
fmt.Println("集合stuSet中包含指定元素200") //输出
} else {
fmt.Println("集合stuSet不包含元素200")
}
//此处flag2=false
flag2, err := redisDb.SIsMember("stuSet", "400").Result()
if flag2 {
fmt.Println("集合stuSet中包含指定元素400")
} else {
fmt.Println("集合stuSet不包含元素400") //输出
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 6.3 并集&交集&差集
func main() {
err := initClient()
if err != nil {
//redis连接错误
panic(err)
}
redisDb.SAdd("blacklist", "Obama") // 向 blacklist 中添加元素
redisDb.SAdd("blacklist", "Hillary") // 再次添加
redisDb.SAdd("blacklist", "the Elder") // 添加新元素
redisDb.SAdd("whitelist", "the Elder") // 向 whitelist 添加元素
// 求交集, 即既在黑名单中, 又在白名单中的元素
names, err := redisDb.SInter("blacklist", "whitelist").Result()
if err != nil {
panic(err)
}
// 获取到的元素是 "the Elder"
fmt.Println("交集结果是: ", names) // [the Elder]
//求交集并将交集保存到 destSet 的集合
res, err := redisDb.SInterStore("destSet", "blacklist", "whitelist").Result()
fmt.Println(res)
//获取交集的值[the Elder]
destStr, _ := redisDb.SMembers("destSet").Result()
fmt.Println(destStr) //[the Elder]
// 求差集
diffStr, err := redisDb.SDiff("blacklist", "whitelist").Result()
if err != nil {
panic(err)
}
fmt.Println("差集结果是: ", diffStr) //[Hillary Obama]
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 6.4 集合删除操作
func main() {
err := initClient()
if err != nil {
//redis连接错误
panic(err)
}
// 将100,200,300批量添加到集合中 集合的元素不能重复
err = redisDb.SAdd("stuSet", 100, 200, 300, 400, 500, 600).Err()
if err != nil {
panic(err)
}
res1, _ := redisDb.SMembers("stuSet").Result()
fmt.Println("res1=", res1) //res1= [100 200 300 400 500 600]
//随机返回集合中的一个元素,并且删除这个元素,这里删除的是400
member1, err := redisDb.SPop("stuSet").Result()
fmt.Println(member1) //400
res2, _ := redisDb.SMembers("stuSet").Result()
fmt.Println("res2=", res2) //res2= [100 200 300 500 600]
// 随机返回集合中的4个元素,并且删除这些元素
member2, err := redisDb.SPopN("stuSet", 4).Result()
fmt.Println(member2) //[]
res3, _ := redisDb.SMembers("stuSet").Result()
fmt.Println("res3=", res3) // [100 200 300 500 600]
//删除集合stuSet名称为300,400的元素,并返回删除的元素个数
member3, err := redisDb.SRem("stuSet", 500, 600).Result()
fmt.Println(member3) //2
res4, _ := redisDb.SMembers("stuSet").Result()
fmt.Println("res4=", res4) //res4= [100 200 300]
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 6.5 集合随机数操作
func main() {
err := initClient()
if err != nil {
//redis连接错误
panic(err)
}
// 将100,200,300批量添加到集合中 集合的元素不能重复
err = redisDb.SAdd("stuSet", 100, 200, 300, 400, 500, 600).Err()
if err != nil {
panic(err)
}
//随机返回集合stuSet中的一个元素
member1, _ := redisDb.SRandMember("stuSet").Result()
fmt.Println(member1) //600
//随机返回集合stuSet中的3个元素
member2, _ := redisDb.SRandMemberN("stuSet", 3).Result()
fmt.Println(member2) //[600 400 500]
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 6.6 SMembersMap & SMove示例
func main() {
err := initClient()
if err != nil {
//redis连接错误
panic(err)
}
// 将100,200,300批量添加到集合中 集合的元素不能重复
err = redisDb.SAdd("stuSet", 100, 200, 300, 400, 500, 600).Err()
err = redisDb.SAdd("resSet", 900).Err()
if err != nil {
panic(err)
}
//把集合里的元素转换成map的key
map1, err:= redisDb.SMembersMap("stuSet").Result()
fmt.Println(map1) //map[100:{} 200:{} 300:{} 400:{} 500:{} 600:{}]
//移动集合stuSet中的一个200元素到集合resSet中去
ok, err:= redisDb.SMove("stuSet", "resSet", 200).Result()
if ok{
fmt.Println("移动数据成功")
}
//返回resSet集合中的所有元素
resSetStr, err := redisDb.SMembers("resSet").Result()
fmt.Println(resSetStr) //[200 900]
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 7 hash数据操作
如果你希望
key/value
的值也能作为hash
结构进行操作,可以选择redis hash
类型。
# 7.1 使用场景举例
如果我们希望缓存一条用户信息(包括用户id、用户名、email字段),希望能够做到局部读写用户信息(例如:读写用户名),也能够读取整条用户信息,那么hash类型就支持这些操作。
Redis hash
操作主要有如下2-3个元素组成
key
-redis key
唯一标识field
-hash
数据的字段名value
- 值,有些操作不需要值
# 7.2 hash数据常用函数汇总
type Cmdable interface {
//根据key和字段名,删除hash字段,支持批量删除hash字段
HDel(key string, fields ...string) *IntCmd
//检测hash字段名是否存在。
HExists(key, field string) *BoolCmd
//根据key和field字段,查询field字段的值
HGet(key, field string) *StringCmd
//根据key查询所有字段和值
HGetAll(key string) *StringStringMapCmd
//根据key和field字段,累加数值。
HIncrBy(key, field string, incr int64) *IntCmd
//根据key和field字段,累加数值。
HIncrByFloat(key, field string, incr float64) *FloatCmd
//根据key返回所有字段名
HKeys(key string) *StringSliceCmd
//根据key,查询hash的字段数量
HLen(key string) *IntCmd
//根据key和多个字段名,批量查询多个hash字段值
HMGet(key string, fields ...string) *SliceCmd
//根据key和多个字段名和字段值,批量设置hash字段值
HMSet(key string, fields map[string]interface{}) *StatusCmd
//根据key和field字段设置,field字段的值
HSet(key, field string, value interface{}) *BoolCmd
//根据key和field字段,查询field字段的值
HSetNX(key, field string, value interface{}) *BoolCmd
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 7.3 HGet & HSet等操作
以下示例包含
HSet
、HGet
、HGetAll
、HMGet
、HMSet
、HSetNX
等操作
func main() {
err := initClient()
if err != nil {
//redis连接错误
panic(err)
}
//根据key和field字段设置,field字段的值。 user_1 是hash key,username 是字段名, admin是字段值
err = redisDb.HSet("user_1", "username", "admin").Err()
if err != nil {
panic(err)
}
//根据key和field字段,查询field字段的值。user_1 是hash key,username是字段名
username, err := redisDb.HGet("user_1", "username").Result()
if err != nil {
panic(err)
}
fmt.Println(username) //admin
//继续往user_1中添加字段password
_ = redisDb.HSet("user_1", "password", "abc123").Err()
// HGetAll 一次性返回key=user_1的所有hash字段和值
data, err := redisDb.HGetAll("user_1").Result()
if err != nil {
panic(err)
}
// data是一个map类型,这里使用使用循环迭代输出
for field, val := range data {
fmt.Println(field, val)
}
// 初始化hash数据的多个字段值
batchData := make(map[string]interface{})
batchData["username"] = "test"
batchData["password"] = 123456
// 一次性保存多个hash字段值
err = redisDb.HMSet("user_2", batchData).Err()
if err != nil {
panic(err)
}
//如果email字段不存在,则设置hash字段值
redisDb.HSetNX("user_2", "email", "ourlang@foxmail.com")
// HMGet支持多个field字段名,意思是一次返回多个字段值
values, err := redisDb.HMGet("user_2", "username", "password", "email").Result()
if err != nil {
panic(err)
}
// values是一个数组
fmt.Println("user_2=", values) //user_2= [test 123456 ourlang@foxmail.com]
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# 7.4 HIncrBy & HIncrByFloat
func main() {
err := initClient()
if err != nil {
//redis连接错误
panic(err)
}
//根据key和field字段设置,field字段的值。 user_1 是hash key,username 是字段名, admin是字段值
err = redisDb.HSet("user_1", "username", "admin").Err()
if err != nil {
panic(err)
}
// 累加count字段的值,一次性累加2, user_1为hash key
count, err := redisDb.HIncrBy("user_1", "count", 2).Result()
if err != nil {
panic(err)
}
fmt.Println(count)
// 累加score字段的值,一次性累加3.2, user_1为hash key
score, err := redisDb.HIncrByFloat("user_1", "score", 3.2).Result()
fmt.Println(score)
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 7.5 HKeys & HLen示例
func main() {
err := initClient()
if err != nil {
//redis连接错误
panic(err)
}
//根据key和field字段设置,field字段的值。 user_1 是hash key,username 是字段名, admin是字段值
err = redisDb.HSet("user_1", "username", "admin").Err()
if err != nil {
panic(err)
}
// 根据key返回所有字段名,keys是一个string数组
keys, err := redisDb.HKeys("user_1").Result()
if err != nil {
panic(err)
}
fmt.Println(keys)
//根据key,查询hash的字段数量
size, err := redisDb.HLen("user_1").Result()
if err != nil {
panic(err)
}
fmt.Println(size)
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 7.6 HDel & HExists示例
func main() {
err := initClient()
if err != nil {
//redis连接错误
panic(err)
}
//根据key和field字段设置,field字段的值。 user_1 是hash key,username 是字段名, admin是字段值
err = redisDb.HSet("user_1", "username", "admin").Err()
if err != nil {
panic(err)
}
// 删除一个字段id
redisDb.HDel("user_1", "score")
// 删除多个字段
redisDb.HDel("user_1", "password", "username")
//检测hash字段名是否存在,存在true 不存在false
exists, err := redisDb.HExists("user_1", "id").Result()
if exists {
fmt.Println(exists)
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 8 有序集合(sorted set)
Redis
有序集合(sorted set)
和集合一样也是string
类型元素的集合,且不允许重复的成员,不同的是每个元素都会关联一个double
类型的分数,这个分数主要用于集合元素排序。
// Z 表示已排序的集合成员
type Z struct {
Score float64 // 分数
Member interface{} // 元素名
}
2
3
4
5
# 8.1 有序集合的常用方法
type Cmdable interface {
// 添加一个或者多个元素到集合,如果元素已经存在则更新分数
ZAdd(key string, members ...Z) *IntCmd
ZAddNX(key string, members ...Z) *IntCmd
ZAddXX(key string, members ...Z) *IntCmd
ZAddCh(key string, members ...Z) *IntCmd
ZAddNXCh(key string, members ...Z) *IntCmd
// 添加一个或者多个元素到集合,如果元素已经存在则更新分数
ZAddXXCh(key string, members ...Z) *IntCmd
//增加元素的分数
ZIncr(key string, member Z) *FloatCmd
ZIncrNX(key string, member Z) *FloatCmd
ZIncrXX(key string, member Z) *FloatCmd
//增加元素的分数,增加的分数必须是float64类型
ZIncrBy(key string, increment float64, member string) *FloatCmd
// 存储增加分数的元素到destination集合
ZInterStore(destination string, store ZStore, keys ...string) *IntCmd
//返回集合元素个数
ZCard(key string) *IntCmd
//统计某个分数范围内的元素个数
ZCount(key, min, max string) *IntCmd
//返回集合中某个索引范围的元素,根据分数从小到大排序
ZRange(key string, start, stop int64) *StringSliceCmd
//ZRevRange的结果是按分数从大到小排序。
ZRevRange(key string, start, stop int64) *StringSliceCmd
//根据分数范围返回集合元素,元素根据分数从小到大排序,支持分页。
ZRangeByScore(key string, opt ZRangeBy) *StringSliceCmd
//根据分数范围返回集合元素,用法类似ZRangeByScore,区别是元素根据分数从大到小排序。
ZRemRangeByScore(key, min, max string) *IntCmd
//用法跟ZRangeByScore一样,区别是除了返回集合元素,同时也返回元素对应的分数
ZRangeWithScores(key string, start, stop int64) *ZSliceCmd
//根据元素名,查询集合元素在集合中的排名,从0开始算,集合元素按分数从小到大排序
ZRank(key, member string) *IntCmd
//ZRevRank的作用跟ZRank一样,区别是ZRevRank是按分数从大到小排序。
ZRevRank(key, member string) *IntCmd
//查询元素对应的分数
ZScore(key, member string) *FloatCmd
//删除集合元素
ZRem(key string, members ...interface{}) *IntCmd
//根据索引范围删除元素。从最低分到高分的(stop-start)个元素
ZRemRangeByRank(key string, start, stop int64) *IntCmd
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# 8.2 ZAdd & ZIncr示例
以下示例包含
ZAdd
、ZAddNX
、ZAddXX
、ZAddCh
、ZAddNXCh
、ZAddXXCh
、ZIncr
、ZIncrNX
、ZIncrXX
、ZIncrBy
、ZInterStore
func main() {
err := initClient()
if err != nil {
//redis连接错误
panic(err)
}
//统计开发语言排行榜
zsetKey := "language_rank"
languages := []redis.Z{
{Score: 90.0, Member: "Golang"},
{Score: 98.0, Member: "Java"},
{Score: 95.0, Member: "Python"},
{Score: 97.0, Member: "JavaScript"},
{Score: 92.0, Member: "C/C++"},
}
// 添加一个或者多个元素到集合,如果元素已经存在则更新分数
num, err := redisDb.ZAdd(zsetKey, languages...).Result()
if err != nil {
fmt.Printf("zadd failed, err:%v\n", err)
return
}
fmt.Printf("ZAdd添加成功 %d 元素\n", num)
// 添加一个元素到集合
redisDb.ZAdd(zsetKey, redis.Z{Score: 87, Member: "Vue"}).Err()
//给元素Vue加上8分,最终vue得分95分
redisDb.ZIncrBy(zsetKey, 8, "Vue")
// 返回从0到-1位置的集合元素, 元素按分数从小到大排序 0到-1代表则返回全部数据
values, err := redisDb.ZRange(zsetKey, 0, -1).Result()
if err != nil {
panic(err)
}
for _, val := range values {
fmt.Println(val)
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# 8.3 ZCount & ZCard示例
func main() {
err := initClient()
if err != nil {
//redis连接错误
panic(err)
}
//统计开发语言排行榜
zsetKey := "language_rank"
//返回集合元素的个数
size, err := redisDb.ZCard(zsetKey).Result()
if err != nil {
panic(err)
}
fmt.Println(size)
//统计某个分数段内的元素个数,这里是查询的95<分数<100的元素个数
count, err := redisDb.ZCount(zsetKey, "95", "100").Result()
if err != nil {
panic(err)
}
fmt.Println(count)
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 8.4 ZRange等相关操作
func main() {
err := initClient()
if err != nil {
//redis连接错误
panic(err)
}
//统计开发语言排行榜
zsetKey := "language_rank"
// 返回从0到-1位置的集合元素, 元素按分数从小到大排序 0到-1代表则返回全部数据
//ZRevRange分数是从打到小排序,用法和ZRange一样
values, err:= redisDb.ZRange(zsetKey, 0, -1).Result()
if err != nil {
panic(err)
}
for _, val := range values {
fmt.Println(val)
}
// 初始化查询条件, Offset和Count用于分页
op := redis.ZRangeBy{
Min:"80", // 最小分数
Max:"100", // 最大分数
Offset:0, // 类似sql的limit, 表示开始偏移量
Count:5, // 一次返回多少数据
}
//根据分数范围返回集合元素,元素根据分数从小到大排序,支持分页。
values, err = redisDb.ZRangeByScore(zsetKey, op).Result()
if err != nil {
panic(err)
}
for _, val := range values {
fmt.Println(val)
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# 8.5 ZRank & ZScore示例
func main() {
err := initClient()
if err != nil {
//redis连接错误
panic(err)
}
//统计开发语言排行榜
zsetKey := "language_rank"
// 查询集合元素Golang的分数
score, _ := redisDb.ZScore(zsetKey, "Golang").Result()
fmt.Println(score)
//根据元素名,查询集合元素在集合中的排名,从0开始算,集合元素按分数从小到大排序
rk, _ := redisDb.ZRank(zsetKey, "Java").Result()
fmt.Println(rk)
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 8.6 删除集合元素
func main() {
err := initClient()
if err != nil {
//redis连接错误
panic(err)
}
//统计开发语言排行榜
zsetKey := "language_rank"
// 删除集合中的元素Java
redisDb.ZRem(zsetKey, "Java")
// 删除集合中的元素Golang和Vue
// 支持一次删除多个元素
redisDb.ZRem(zsetKey, "Golang", "Vue")
//根据索引范围删除元素
//集合元素按分数排序,从最低分到高分,删除第0个元素到第5个元素。
// 这里相当于删除最低分的几个元素
redisDb.ZRemRangeByRank(zsetKey, 0, 5)
// 位置参数写成负数,代表从高分开始删除。
// 这个例子,删除最高分数的两个元素,-1代表最高分数的位置,-2第二高分,以此类推。
redisDb.ZRemRangeByRank(zsetKey, -1, -2)
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 9 事务处理
redis
事务可以一次执行多个命令,事务中的命令要么全部被执行,要么全部都不执行。以下示例是以Pipeline
的方式操作事务,接口结构如下:
type Pipeliner interface {
StatefulCmdable
Do(args ...interface{}) *Cmd
Process(cmd Cmder) error
Close() error
Discard() error
Exec() ([]Cmder, error)
}
2
3
4
5
6
7
8
# 9.1 redis事务常用函数
//以Pipeline的方式操作事务
TxPipeline() Pipeliner
Watch - redis乐观锁支持
2
3
# 9.2 TxPipeline示例
func main() {
err := initClient()
if err != nil {
//redis连接错误
panic(err)
}
//统计开发语言排行榜
zsetKey := "language_rank"
// 开启一个TxPipeline事务
pipe := redisDb.TxPipeline()
// 执行事务操作,可以通过pipe读写redis
incr := pipe.Incr(zsetKey)
pipe.Expire(zsetKey, time.Hour)
// 通过Exec函数提交redis事务
_, err = pipe.Exec()
// 提交事务后,我们可以查询事务操作的结果
// 前面执行Incr函数,在没有执行exec函数之前,实际上还没开始运行。
fmt.Println(incr.Val(), err)
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 9.3 watch
redis
乐观锁支持,可以通过watch
监听一些Key
,如果这些key
的值没有被其他人改变的话,才可以提交事务。
func main() {
err := initClient()
if err != nil {
//redis连接错误
panic(err)
}
// 定义一个回调函数,用于处理事务逻辑
fn := func(tx *redis.Tx) error {
// 先查询下当前watch监听的key的值
v, err := tx.Get("key").Result()
if err != nil && err != redis.Nil {
return err
}
// 这里可以处理业务
fmt.Println(v)
// 如果key的值没有改变的话,Pipelined函数才会调用成功
_, err = tx.Pipelined(func(pipe redis.Pipeliner) error {
// 在这里给key设置最新值
pipe.Set("key", "new value", 0)
return nil
})
return err
}
// 使用Watch监听一些Key, 同时绑定一个回调函数fn, 监听Key后的逻辑写在fn这个回调函数里面
// 如果想监听多个key,可以这么写:client.Watch(fn, "key1", "key2", "key3")
redisDb.Watch(fn, "key")
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30