go-redis文档

8,819 阅读22分钟

「这是我参与11月更文挑战的第6天,活动详情查看:2021最后一次更文挑战」。

go-redis文档

1 Redis优势

更多Redis学习请参考本网站Redis教程

::: tip 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连接成功")
}

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
}

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
}

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
}

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)
	}
}

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]
}

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
}

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")
}

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
   }

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]
}

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)
}

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]
}

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]
}

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]
}

6 集合set操作

redis的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
}

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]
}

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") //输出
	}
}

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]
}

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]
}

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]
}

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]
}

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
}

7.3 HGet & HSet等操作

以下示例包含HSetHGetHGetAllHMGetHMSetHSetNX等操作

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]
}

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)
}

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)
}

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)
	}
}

8 有序集合(sorted set)

Redis有序集合(sorted set)和集合一样也是string类型元素的集合,且不允许重复的成员,不同的是每个元素都会关联一个double类型的分数,这个分数主要用于集合元素排序。

// Z 表示已排序的集合成员
type Z struct {
	Score  float64  // 分数
	Member interface{} // 元素名
}

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
}

8.2 ZAdd & ZIncr示例

以下示例包含ZAddZAddNXZAddXXZAddChZAddNXChZAddXXChZIncrZIncrNXZIncrXXZIncrByZInterStore

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)
	}
}

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)
}

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)
	}
}

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)
}

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)
}

9 事务处理

redis事务可以一次执行多个命令,事务中的命令要么全部被执行,要么全部都不执行。以下示例是以Pipeline的方式操作事务,接口结构如下:

type Pipeliner interface {
	StatefulCmdable
	Do(args ...interface{}) *Cmd
	Process(cmd Cmder) error
	Close() error
	Discard() error
	Exec() ([]Cmder, error)
}

9.1 redis事务常用函数

//以Pipeline的方式操作事务
TxPipeline() Pipeliner
Watch - redis乐观锁支持

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)
}

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")
}

10 更多资料参考

Redis教程

go-redis文档