【Java转Go】快速上手学习笔记(八)之Redis篇一(go-redis)

go-redis

这篇是关于Redis的笔记。这里用的是 go-redis ,要先下载

go get github.com/go-redis/redis

连接

使用 redis.NewClient() 创建连接,这个函数参数是一个叫 Options 的结构体,这个结构体的属性字段就是设置连接redis的相关参数;创建连接返回一个 Client

Options结构体

下面是 Options结构体 的属性及其作用:

Network            string            // 网络类型 tcp 或者 unix,默认是 tcp
Addr               string            // redis地址,格式 host:port
OnConnect          func(*Conn) error // 新建一个redis连接的时候,会回调这个函数
Password           string            // redis密码,redis server没有设置可以为空。
DB                 int               // redis数据库,序号从0开始,默认是0,可以不用设置
MaxRetries         int               // redis操作失败最大重试次数,默认不重试。
MinRetryBackoff    time.Duration     // 最小重试时间间隔,默认是 8ms,-1 表示关闭
MaxRetryBackoff    time.Duration     // 最大重试时间间隔,默认是 512ms,-1 表示关闭
DialTimeout        time.Duration     // redis连接超时时间,默认是 5 秒
ReadTimeout        time.Duration     // socket读取超时时间,默认 3 秒
WriteTimeout       time.Duration     // socket写超时时间
PoolSize           int               // redis连接池的最大连接数,默认连接池大小等于 cpu个数 * 10
MinIdleConns       int               // redis连接池最小空闲连接数
MaxConnAge         time.Duration     // redis连接最大的存活时间,默认不会关闭过时的连接
PoolTimeout        time.Duration     // 当你从redis连接池获取一个连接之后,连接池最多等待这个拿出去的连接多长时间。默认是等待 ReadTimeout + 1 秒
IdleTimeout        time.Duration     // redis连接池多久会关闭一个空闲连接,默认是 5 分钟,-1 则表示关闭这个配置项
IdleCheckFrequency time.Duration     // 多长时间检测一下,空闲连接,默认是 1 分钟,-1 表示关闭空闲连接检测
readOnly           bool              // 只读设置,如果设置为true,redis只能查询缓存不能更新。

知道了连接redis的相关参数,我们创建一个连接:

// 连接到redis
conn := redis.NewClient(&redis.Options{
	Addr:     "127.0.0.1:6379",
	Password: "key#1234",
	//DialTimeout: 10, // 连接超时(s),默认是5秒
})
fmt.Println("连接成功", conn)

这里我要说一下, DialTimeout: 10 这一句!!!自己手动设置连接超时的时间,这个设置太坑了!!!

我改成10秒、20秒,连接会出问题,经常出现连接超时的问题,还有就是我想设置某个key的过期时间都死活设置不了!!!

conn.Set("key", "value", time.Minute*60)  这种方式死活设置不了,只能设置成0,也就是永不过期,我还在想go-redis没办法设置过期吗???

conn.Expire("key", time.Minute*60) 用这种方式也一样,总是报错,说连接超时,我真的麻了,一直在想怎么会超时呢?怎么就会超时呢!!!

我翻源码、问百度也找不到原因,还有就是按照我的理解 DialTimeout 是可以设置成其他秒数的,我在Java中,连redis就是设置的10秒,所以我一直不觉得这里设置成10秒有问题。

这就导致我半天找不到是什么原因!!!后面我尝试把这句注释掉了,发现居然可以了!!!知道我当时有多无语吗!!!我真的会谢!!!

我都不知道为什么不能设置成其他秒数。

我也不知道为什么会有这个问题。

基本使用(Get、Set)

func 基本使用(conn *redis.Client) {
	// 设置过期时间,要注意,它的单位不是秒,是毫秒

	// 1、Set:向redis的一个key设置值
	err := conn.Set("go-demo:name", "符华", time.Minute*60).Err()
	if err != nil { // 检测设置是否成功
		panic(err)
	}

	// 2、Get:读取一个key的值
	s, err := conn.Get("go-demo:name").Result()
	if err != nil { // 检测设置是否成功
		panic(err)
	}
	fmt.Println("go-demo:name =", s)

	// 3、GetSet:向redis的一个key设置值(新值),并返回这个key的旧值
	oldVal, err := conn.GetSet("go-demo:name", "赤鸢").Result()
	if err != nil {
		panic(err)
	}
	fmt.Println("go-demo:name的旧值 =", oldVal) // 打印key的旧值

	// 4、SetNX:如果一个key不存在,则设置这个key的值
	err := conn.SetNX("go-demo:password", "123456", 0).Err()
	if err != nil {
		panic(err)
	}

	// 5、MSet:批量设置key的值
	err := conn.MSet("go-demo:phone", "13254784511", "go-demo:age", "20", "go-demo:address", "广东省惠州市").Err()
	if err != nil {
		panic(err)
	}

	// 6、MGet:批量查询key的值:可以传入任意个key,一次性返回多个值(如果key不存在,则返回nil)
	vals, err := conn.MGet("go-demo:name", "go-demo:password", "go-demo:phone").Result()
	if err != nil {
		panic(err)
	}
	fmt.Println(vals)

	// 7、Incr、IncrBy、IncrByFloat(和IncrBy的区别是累加的是浮点数):对一个key的数值进行递增操作
	val, err := conn.Incr("go-demo:age").Result() // Incr函数每次加一
	if err != nil {
		panic(err)
	}
	fmt.Println("go-demo:age 最新值 =", val)

	val, err := conn.IncrBy("go-demo:age", 2).Result() // IncrBy、IncrByFloat函数,可以指定每次递增多少
	if err != nil {
		panic(err)
	}
	fmt.Println("go-demo:age 最新值 =", val)

	// 8、Decr、DecrBy:对一个key的数值进行递减操作
	val, err := conn.Decr("go-demo:age").Result() // Decr函数每次减一
	if err != nil {
		panic(err)
	}
	fmt.Println("go-demo:age 最新值 =", val)

	val, err := conn.DecrBy("go-demo:age", 2).Result() // DecrBy函数,可以指定每次递减多少
	if err != nil {
		panic(err)
	}
	fmt.Println("go-demo:age 最新值 =", val)

	// 9、Del:删除key,支持批量删除
	conn.Del("go-demo:age")                                 // 删除key
	err := conn.Del("go-demo:age", "go-demo:address").Err() // 删除多个key, Del函数支持删除多个key
	if err != nil {
		panic(err)
	}

	// 10、Expire:设置key的过期时间,单位秒
	_, err := conn.Expire("go-demo:address", time.Minute*6).Result()
	if err != nil {
		panic(err)
	}
}

hash使用

func hash使用(conn *redis.Client) {
	// 初始化hash数据的多个字段值
	var myMap = make(map[string]interface{})
	myMap["id"] = 1
	myMap["name"] = "符华"
	myMap["age"] = 20
	myMap["token"] = "Tjkl=sdjkuioOTHJg5412=erweYTJKkl65sdfl"

	// 1、HMSet:根据key和多个字段名和字段值,批量设置hash字段值
	err := conn.HMSet("go-demo:login:1", myMap).Err() // 一次性保存多个hash字段值
	if err != nil {
		panic(err)
	}

	// 2、HMGet:根据key和多个字段名,批量查询多个hash字段值
	vals, err := conn.HMGet("go-demo:login:1", "name", "token").Result() // HMGet支持多个field字段名,意思是一次返回多个字段值
	if err != nil {
		panic(err)
	}
	fmt.Println(vals)

	// 3、HSet:根据key和field字段设置,field字段的值
	err := conn.HSet("go-demo:login:1", "name", "赤鸢").Err()
	if err != nil {
		panic(err)
	}

	// 4、HGet:根据key和field字段,查询field字段的值
	name, err := conn.HGet("go-demo:login:1", "name").Result()
	if err != nil {
		panic(err)
	}
	fmt.Println(name)

	// 5、HGetAll:根据key查询所有字段和值
	data, err := conn.HGetAll("go-demo:login:1").Result()
	if err != nil {
		panic(err)
	}
	for field, val := range data { // data是一个map类型,这里使用使用循环迭代输出
		fmt.Println(field, val)
	}

	// 6、HIncrBy:根据key和field字段,递增字段的数值,可以指定递增次数,每次递增+1
	count, err := conn.HIncrBy("go-demo:login:1", "age", 2).Result()
	if err != nil {
		panic(err)
	}
	fmt.Println(count)

	// 7、HKeys:根据key返回所有字段名
	keys, err := conn.HKeys("go-demo:login:1").Result()
	if err != nil {
		panic(err)
	}
	fmt.Println(keys)

	// 8、HLen:根据key,查询hash的字段数量
	size, err := conn.HLen("go-demo:login:1").Result()
	if err != nil {
		panic(err)
	}
	fmt.Println(size)

	// 9、HSetNX:如果field字段不存在,则设置hash字段值
	err := conn.HSetNX("go-demo:login:1", "phone", "13452117452").Err()
	if err != nil {
		panic(err)
	}

	// 10、HDel:根据key和字段名,删除hash字段,支持批量删除hash字段
	conn.HDel("go-demo:login:1", "phone")        // 删除一个字段id
	conn.HDel("go-demo:login:1", "age", "phone") // 删除多个字段

	// 11、HExists:检测hash字段名是否存在。
	err := conn.HExists("go-demo:login:1", "id").Err()
	if err != nil {
		panic(err)
	}
}

list使用

func list使用(conn *redis.Client) {
	// 1、LPush:从列表左边插入数据
	conn.LPush("go-demo:userList", "符华")                          // 插入一个数据
	err := conn.LPush("go-demo:userList", "赤鸢", "云墨", "炽翎").Err() // LPush支持一次插入任意个数据
	if err != nil {
		panic(err)
	}

	// 2、RPush:从列表右边插入数据
	conn.RPush("go-demo:userList", "白夜")                    // 插入一个数据
	err := conn.RPush("go-demo:userList", "月轮", "迅羽").Err() // 支持一次插入任意个数据
	if err != nil {
		panic(err)
	}

	// 3、LPushX:跟LPush的区别是,仅当列表存在的时候才插入数据,用法完全一样。
	// 4、RPushX:跟RPush的区别是,仅当列表存在的时候才插入数据, 他们用法一样

	// 5、LPop:从列表左边删除第一个数据,并返回删除的数据
	val, err := conn.LPop("go-demo:userList").Result()
	if err != nil {
		panic(err)
	}
	fmt.Println(val)

	// 6、RPop:从列表的右边删除第一个数据,并返回删除的数据
	val, err := conn.RPop("go-demo:userList").Result()
	if err != nil {
		panic(err)
	}
	fmt.Println(val)

	// 7、LLen:返回列表的大小
	val, err := conn.LLen("go-demo:userList").Result()
	if err != nil {
		panic(err)
	}
	fmt.Println(val)

	// 8、LRange:返回列表的一个范围内的数据,也可以返回全部数据(可用于分页)
	vals, err := conn.LRange("go-demo:userList",0,-1).Result() // 返回从0开始到-1位置之间的数据,意思就是返回全部数据
	if err != nil {
		panic(err)
	}
	fmt.Println(vals)

	// 9、LRem:删除列表中的数据
	dels, err := conn.LRem("go-demo:userList",1,100).Result() // 从列表左边开始,删除100, 如果出现重复元素,仅删除1次,也就是删除第一个
	if err != nil {
		panic(err)
	}
	fmt.Println(dels)
	conn.LRem("go-demo:userList",2,100) // 如果存在多个100,则从列表左边开始删除2个100
	conn.LRem("go-demo:userList",-2,100) // 如果存在多个100,则从列表右边开始删除2个100(第二个参数负数表示从右边开始删除几个等于100的元素)
	conn.LRem("go-demo:userList",0,100) // 如果存在多个100,第二个参数为0,表示删除所有元素等于100的数据

	// 10、LIndex:根据索引坐标,查询列表中的数据
	val, err := conn.LIndex("go-demo:userList",5).Result() // 列表索引从0开始计算,这里返回第6个元素
	if err != nil {
		panic(err)
	}
	fmt.Println(val)

	// 11、LInsert:在指定位置插入数据
	err := conn.LInsert("go-demo:userList", "before", 5, 4).Err() // 在列表中5的前面插入4,before是之前的意思
	if err != nil {
		panic(err)
	}
	conn.LInsert("go-demo:userList", "before", "白夜", "玄衣素裳") // 在列表中 白夜 元素的前面插入 玄衣素裳
	conn.LInsert("go-demo:userList", "after", "白夜", "春风之旅")  // 在列表中 白夜 元素的后面插入 春风之旅
}

set使用

func set使用(conn *redis.Client) {
	// 1、SAdd:添加集合元素
	err := conn.SAdd("go-demo:roleList","管理员").Err()
	if err != nil {
		panic(err)
	}
	conn.SAdd("go-demo:roleList","VIP", "SVIP", "VVIP")

	// 2、SCard:获取集合元素个数
	size, err := conn.SCard("go-demo:roleList").Result()
	if err != nil {
		panic(err)
	}
	fmt.Println(size)

	// 3、SIsMember:判断元素是否在集合中
	ok, _ := conn.SIsMember("go-demo:roleList", "VVIP").Result()
	if ok {
		fmt.Println("集合包含指定元素")
	}

	// 4、SMembers:获取集合中所有的元素
	es, _ := conn.SMembers("go-demo:roleList").Result()
	fmt.Println(es) // 返回的es是string数组

	// 5、SRem:删除集合元素,可以批量删除
	conn.SRem("go-demo:roleList", "管理员")
	conn.SRem("go-demo:roleList", "VIP", "20SVIP19")

	// 6、SPop、SPopN:随机返回集合中的元素,并且删除返回的元素
	val, _ := conn.SPop("go-demo:roleList").Result() // 随机返回集合中的一个元素,并且删除这个元素
	fmt.Println(val)
	vals, _ := conn.SPopN("go-demo:roleList", 5).Result() // 随机返回集合中的5个元素,并且删除这些元素
	fmt.Println(vals)
}

sortedSet使用

这个有序集合,需要用到一个结构体:redis.Z

type Z struct {
	Score  float64 // 分数
	Member interface{} // 元素名
}
func sortedSet使用(conn *redis.Client) {
	// 1、ZAdd:添加一个或者多个元素到集合,如果元素已经存在则更新分数
	err := conn.ZAdd("go-demo:scores", redis.Z{4.5,"admin"}).Err() // 添加一个集合元素到集合中, 这个元素的分数是4.5,元素名是admin
	if err != nil {
		panic(err)
	}

	// 2、ZCard:返回集合元素个数
	size, err := conn.ZCard("go-demo:scores").Result()
	if err != nil {
		panic(err)
	}
	fmt.Println(size)

	// 3、ZCount:统计某个分数范围内的元素个数:默认第二,第三个参数是大于等于和小于等于的关系;如果加上 ( 则表示大于或者小于,相当于去掉了等于关系。
	size, err := conn.ZCount("go-demo:scores", "2","5").Result() // 返回: 2<=分数<=5 的元素个数, 注意:"1", "5"两个参数是字符串
	size, err := conn.ZCount("go-demo:scores", "(1","5").Result() // 返回: 1<分数<=5 的元素个数
	if err != nil {
		panic(err)
	}
	fmt.Println(size)

	// 4、ZIncrBy:增加元素的分数
	conn.ZIncrBy("go-demo:scores", 2,"admin") // 给元素5,加上2分

	// 5、ZRange:返回集合中某个索引范围的元素,根据分数从小到大排序
	// 6、ZRevRange:用法跟ZRange一样,区别是ZRevRange的结果是按分数从大到小排序。
	vals, err := conn.ZRange("go-demo:scores", 0,-1).Result() // 返回从0到-1位置的集合元素,元素按分数从小到大排序( 0到-1代表则返回全部数据)
	if err != nil {
		panic(err)
	}
	for _, val := range vals {
		fmt.Println(val)
	}

	// 7、ZRangeByScore:根据分数范围返回集合元素,元素根据分数从小到大排序,支持分页。
	// 8、ZRevRangeByScore:用法类似ZRangeByScore,区别是元素根据分数从大到小排序。
	// 初始化查询条件, Offset和Count用于分页
	op := redis.ZRangeBy{
		Min:"2", // 最小分数
		Max:"10", // 最大分数
		Offset:0, // 类似sql的limit, 表示开始偏移量
		Count:5, // 一次返回多少数据
	}
	vals, err := conn.ZRangeByScore("go-demo:scores", op).Result()
	if err != nil {
		panic(err)
	}
	for _, val := range vals {
		fmt.Println(val)
	}

	// 9、ZRangeByScoreWithScores:用法跟ZRangeByScore一样,区别是除了返回集合元素,同时也返回元素对应的分数
	// 初始化查询条件, Offset和Count用于分页
	op := redis.ZRangeBy{
		Min:"2", // 最小分数
		Max:"10", // 最大分数
		Offset:0, // 类似sql的limit, 表示开始偏移量
		Count:5, // 一次返回多少数据
	}
	vals, err := conn.ZRangeByScoreWithScores("go-demo:scores", op).Result()
	if err != nil {
		panic(err)
	}
	for _, val := range vals {
		fmt.Println(val.Member) // 集合元素
		fmt.Println(val.Score) // 分数
	}

	// 10、ZRem:删除集合元素,支持批量删除
	conn.ZRem("go-demo:scores", "admin") // 删除集合中的元素tizi
	conn.ZRem("go-demo:scores", "admin", "fuhua") // 支持一次删除多个元素

	// 11、ZRemRangeByRank:根据索引范围删除元素
	conn.ZRemRangeByRank("go-demo:scores", 0, 5) // 集合元素按分数排序,从最低分到高分,删除第0个元素到第5个元素(这里相当于删除最低分的几个元素)。
	// 这个例子,删除最高分数的两个元素,-1代表最高分数的位置,-2第二高分,以此类推。
	conn.ZRemRangeByRank("go-demo:scores", -1, -2) // 位置参数写成负数,代表从高分开始删除。

	// 12、ZRemRangeByScore:根据分数范围删除元素
	conn.ZRemRangeByScore("go-demo:scores", "2", "5") // 删除范围: 2<=分数<=5 的元素
	conn.ZRemRangeByScore("go-demo:scores", "2", "(5") // 删除范围: 2<=分数<5 的元素

	// 13、ZScore:查询元素对应的分数
	score, _ := conn.ZScore("go-demo:scores", "admin").Result()
	fmt.Println(score)

	// 14、ZRank:根据元素名,查询集合元素在集合中的排名,从0开始算,集合元素按分数从小到大排序
	// 15、ZRevRank的作用跟ZRank一样,区别是ZRevRank是按分数从大到小排序。
	rk, _ := conn.ZRank("go-demo:scores", "admin").Result()
	fmt.Println(rk)
}

ok,以上就是本篇笔记的全部内容了,主要是 go-redis 连接redis的一些操作、基本使用。其实我个人做项目用的比较频繁的是普通的 get、set,还有 hash 和 list ,像集合和有序集合用的很少,几乎没怎么用过。

比如我做的springboot项目,用到redis的场景大部分都是存验证码、登录校验、token校验、存登录用户信息这些,还有少部分是没有mysql或sqlite数据库,而是用redis当作数据库来用的。

你可能感兴趣的:(Go,golang,go,redis,go-redis)