# GO操作Redis ## 摘要 > Golang go-Redis - [官方](https://github.com/go-redis/redis "官方") - [docs文档](https://godoc.org/github.com/go-redis/redis "docs文档") ## 安装 ``` go get -u github.com/go-redis/redis ``` ## 初始化连接 ### 单Redis节点 ``` package main import "github.com/go-redis/redis" //创建一个全局的redis客户端实例Rdb var Rdb *redis.Client func initRdb()(err error){ //这里这里不要用 := ,否则只是赋值给了临时变量 Rdb = redis.NewClient(&redis.Options{ Addr: "192.168.116.90:6379", // 留空为没设密码 Password: "", // 默认的DB 为 0 DB: 0, // 连接池大小 PoolSize: 100, }) //测试 redis 是否可以连接 _,err = Rdb.Ping().Result() return err } func main() { if err := initRdb();err != nil{ panic(err) } defer Rdb.Close() } ``` ### Redis哨兵模式 ``` //创建一个全局的redis客户端实例Rdb var Rdb *redis.Client func initRdb()(err error){ //这里这里不要用 := ,否则只是赋值给了临时变量 Rdb = redis.NewFailoverClient(&redis.FailoverOptions{ MasterName: "master", SentinelAddrs: []string{"192.168.116.90:6379", "192.168.116.90:6380", "192.168.116.90:6381"}, }) // 测试 redis 是否可以连接 _, err = Rdb.Ping().Result() return err } func main() { if err := initRdb();err != nil{ panic(err) } defer Rdb.Close() } ``` ### Redis Cluster集群模式 > 集群模式的客户端实例与单节点是不一样的 *redis.ClusterClient ``` //创建一个全局的redis客户端实例Rdb var Rdb *redis.ClusterClient func initRdb()(err error){ //这里这里不要用 := ,否则只是赋值给了临时变量 Rdb = redis.NewClusterClient(&redis.ClusterOptions{ Addrs: []string{"192.168.110.131:6379","192.168.110.131:6380","192.168.110.131:6381"}, }) //测试 redis 是否可以连接 _,err = Rdb.Ping().Result() return err } func main() { if err := initRdb();err != nil{ panic(err) } defer Rdb.Close() } ``` ## 操作 ### 字符串操作 > Set 操作的函数 ``` func redisSet(key string,value interface{},expr time.Duration){ err := Rdb.Set(key,value,expr).Err() if err != nil{ fmt.Printf("redisSet Failed Error %v\n",err) return } fmt.Printf("Redis Set key: [%s] value: [%v] Success\n",key,value) } func main() { if err := initRdb();err != nil{ panic(err) } fmt.Println("Connection Redis Client Success!") defer Rdb.Close() //expr参数代表过期时间,0为永不过期 redisSet("RedisKey","value1",0) } 输出: Connection Redis Client Success! Redis Set key: [RedisKey] value: [value1] Success ``` > Get 操作的函数 ``` func redisGet(key string) { value ,err := Rdb.Get(key).Result() // 首先使用redis.Nil 判断key是否存在 if err == redis.Nil{ fmt.Printf("Key: [%s] is Null\n",key) //再判断 err != nil }else if err != nil { fmt.Printf("redisGet key failed error %v\n",err) }else{ fmt.Printf("key: [%s] value:[%v]\n",key,value) } } func main() { if err := initRdb();err != nil{ panic(err) } fmt.Println("Connection Redis Client Success!") defer Rdb.Close() //不存在的key redisGet("redis") //存在的key值 redisGet("RedisKey") } 输出: Connection Redis Client Success! Key: [redis] is Null key: [RedisKey] value:[value1] ``` > TTL 获取过期时间 ``` func main() { if err := initRdb();err != nil{ panic(err) } fmt.Println("Connection Redis Client Success!") defer Rdb.Close() //插入键hello,并设置60秒过期 redisSet("hello","world",60 * time.Second) time.Sleep(5 * time.Second) //获取过期时间 tm,_ := Rdb.TTL("hello").Result() fmt.Println(tm) } 输出: Connection Redis Client Success! Redis Set key: [hello] value: [world] Success 55s ``` > SetNX 当键不存在的时候才赋值 ``` func main() { if err := initRdb();err != nil{ panic(err) } fmt.Println("Connection Redis Client Success!") defer Rdb.Close() err := Rdb.Set("hello1","world1",0).Err() if err != nil{ fmt.Println("set1 failed") } err = Rdb.SetNX("hello1","world2",0).Err() if err != nil{ fmt.Println("setNx Failed") } redisGet("hello1") } 输出 Connection Redis Client Success! key: [hello1] value:[world1] ``` > Incr 自增 ``` func main() { if err := initRdb();err != nil{ panic(err) } fmt.Println("Connection Redis Client Success!") defer Rdb.Close() //设置一个永不过期变量counter Rdb.SetNX("counter",0,0).Result() err := Rdb.Incr("counter").Err() if err != nil{ fmt.Println("incr failed") return } redisGet("counter") } 输出 C:\Users\xx\Desktop\git\sql>go run main.go Connection Redis Client Success! key: [counter] value:[1] C:\Users\xx\Desktop\git\sql>go run main.go Connection Redis Client Success! key: [counter] value:[2] ``` ### 列表 > LPush | RPush 添加 ``` func main() { if err := initRdb();err != nil{ panic(err) } fmt.Println("Connection Redis Client Success!") defer Rdb.Close() //从右入栈 Rdb.RPush("list_test","message").Err() Rdb.RPush("list_test","message1").Err() //从左入栈 Rdb.LPush("list_test","message2").Err() //查看所有元素 lrange key start end 获取指定范围的元素0(n),谨慎使用 v,_ := Rdb.LRange("list_test",0,-1).Result() fmt.Println(v) } ``` > LSet 更新某个索引的值 ``` func main() { if err := initRdb();err != nil{ panic(err) } fmt.Println("Connection Redis Client Success!") defer Rdb.Close() Rdb.LSet("list_test",2,"message set") //查看所有元素 lrange key start end 获取指定范围的元素0(n),谨慎使用 v,_ := Rdb.LRange("list_test",0,-1).Result() fmt.Println(v) } ``` > LLen 获取列表长度 ``` func main() { if err := initRdb();err != nil{ panic(err) } fmt.Println("Connection Redis Client Success!") defer Rdb.Close() //查看所有元素 lrange key start end 获取指定范围的元素0(n),谨慎使用 v,_ := Rdb.LRange("list_test",0,-1).Result() fmt.Println(v) //获取长度 len,_ := Rdb.LLen("list_test").Result() fmt.Println(len) } ``` > LPop | RPop 出栈 pop 操作,没有阻塞 ``` func main() { if err := initRdb();err != nil{ panic(err) } fmt.Println("Connection Redis Client Success!") defer Rdb.Close() //从左出栈 Rdb.LPop("list_test").Err() //从右出栈 Rdb.RPop("list_test").Err() //查看所有元素 lrange key start end 获取指定范围的元素0(n),谨慎使用 v,_ := Rdb.LRange("list_test",0,-1).Result() fmt.Println(v) //获取长度 len,_ := Rdb.LLen("list_test").Result() fmt.Println(len) } ``` > LIndex 获取指定索引的元素值 ``` func main() { if err := initRdb();err != nil{ panic(err) } fmt.Println("Connection Redis Client Success!") defer Rdb.Close() v,_ := Rdb.LIndex("list_test",0).Result() fmt.Println(v) } ``` > LInsert 在指定元素的前面或者后面添加新的元素 LInsert [key before | after item newitem] ``` func main() { if err := initRdb();err != nil{ panic(err) } fmt.Println("Connection Redis Client Success!") defer Rdb.Close() v,_ := Rdb.LRange("list_test",0,-1).Result() fmt.Println(v) //指定在message元素之前添加元素hello Rdb.LInsert("list_test","before","message","hello") //指定在message元素之后添加元素hello1 Rdb.LInsert("list_test","after","message","hello1") v,_ = Rdb.LRange("list_test",0,-1).Result() fmt.Println(v) } 输出 Connection Redis Client Success! [message] [hello message hello1] ``` > lrem key count value 删除指定个数值为value的元素 ``` count = 0 删除所有值为value的元素 count > 0 :从左到右删除count个值为value的元素 count < 0 :从右到左删除count个值为value的元素 func main() { if err := initRdb();err != nil{ panic(err) } fmt.Println("Connection Redis Client Success!") defer Rdb.Close() v,_ := Rdb.LRange("list_test",0,-1).Result() fmt.Println(v) Rdb.LRem("list_test",0,"message") v,_ = Rdb.LRange("list_test",0,-1).Result() fmt.Println(v) } 输出: Connection Redis Client Success! [hello message hello1] [hello hello1] ``` > ltrim key start end 保留指定范围的元素 ``` func main() { if err := initRdb();err != nil{ panic(err) } fmt.Println("Connection Redis Client Success!") defer Rdb.Close() v,_ := Rdb.LRange("list_test",0,-1).Result() fmt.Println(v) //只保留索引为0-4的键值,注意收尾都包括,也就是索引0和索引4都包括在内 Rdb.LTrim("list_test",0,4) v,_ = Rdb.LRange("list_test",0,-1).Result() fmt.Println(v) } 输出: Connection Redis Client Success! [hello 0 4 3 2 1] [hello 0 4 3 2] ``` ### Hash > HMSet 添加 ``` func main() { if err := initRdb();err != nil{ panic(err) } fmt.Println("Connection Redis Client Success!") defer Rdb.Close() datas := map[string]interface{}{ "name": "xxx", "sex": 1, "age": 22, "tel": "123456789111", } Rdb.HMSet("hash_test",datas) } ``` > HMGet 获取对应键內指定字段的值 ``` func main() { if err := initRdb();err != nil{ panic(err) } fmt.Println("Connection Redis Client Success!") defer Rdb.Close() ret,_ := Rdb.HMGet("hash_test","name","sex","age","tel").Result() fmt.Println(ret) } 输出: Connection Redis Client Success! [xxx 1 22 123456789111] ``` > HSet 设置对应键內某个字段的值 ``` func main() { if err := initRdb();err != nil{ panic(err) } fmt.Println("Connection Redis Client Success!") defer Rdb.Close() ret,_ := Rdb.HMGet("hash_test","name","sex","age","tel").Result() fmt.Println(ret) Rdb.HSet("hash_test","age",33) ret,_ = Rdb.HMGet("hash_test","name","sex","age","tel").Result() fmt.Println(ret) } 输出: Connection Redis Client Success! [xxx 1 22 123456789111] [xxx 1 33 123456789111] ``` > HGet 获取对应键某个字段的值 ``` func main() { if err := initRdb();err != nil{ panic(err) } fmt.Println("Connection Redis Client Success!") defer Rdb.Close() ret, _ := Rdb.HGet("hash_test","name").Result() fmt.Println(ret) } 输出: Connection Redis Client Success! xxx ``` > HSetNX 用于设置键內不存在key的值,如果key存在,不会设置成功 ``` func main() { if err := initRdb();err != nil{ panic(err) } fmt.Println("Connection Redis Client Success!") defer Rdb.Close() ret,_ := Rdb.HMGet("hash_test","name","sex","age","tel").Result() fmt.Println(ret) Rdb.HSetNX("hash_test","age",44) ret,_ = Rdb.HMGet("hash_test","name","sex","age","tel").Result() fmt.Println(ret) } 输出: Connection Redis Client Success! [xxx 1 33 123456789111] [xxx 1 33 123456789111] ``` >HGetAll 获取指定key对应的hash对象 ``` func main() { if err := initRdb();err != nil{ panic(err) } fmt.Println("Connection Redis Client Success!") defer Rdb.Close() ret, _ := Rdb.HGetAll("hash_test").Result() fmt.Println(ret) } 输出: Connection Redis Client Success! map[age:33 name:xxx sex:1 tel:123456789111] ``` >HDel 删除指定键对应的hash对象的某个字段 ``` func main() { if err := initRdb();err != nil{ panic(err) } fmt.Println("Connection Redis Client Success!") defer Rdb.Close() ret, _ := Rdb.HGetAll("hash_test").Result() fmt.Println(ret) Rdb.HDel("hash_test","sex") ret, _ = Rdb.HGetAll("hash_test").Result() fmt.Println(ret) } 输出: Connection Redis Client Success! map[age:33 name:xxx sex:1 tel:123456789111] map[age:33 name:xxx tel:123456789111] ``` >HExists 判断指定键对应的hash对象是否有某个字段 ``` func main() { if err := initRdb();err != nil{ panic(err) } fmt.Println("Connection Redis Client Success!") defer Rdb.Close() ret,_ := Rdb.HExists("hash_test","name").Result() fmt.Println(ret) ret,_ = Rdb.HExists("hash_test","name1").Result() fmt.Println(ret) } 输出: Connection Redis Client Success! true false ``` >HLen 获取指定键对应hash对象的字段个数 ``` func main() { if err := initRdb();err != nil{ panic(err) } fmt.Println("Connection Redis Client Success!") defer Rdb.Close() ret,_ := Rdb.HLen("hash_test").Result() fmt.Println(ret) } 输出: Connection Redis Client Success! 3 ``` >HVals 获取指定键对应的hash对象所有的value值 ``` func main() { if err := initRdb();err != nil{ panic(err) } fmt.Println("Connection Redis Client Success!") defer Rdb.Close() ret,_ := Rdb.HVals("hash_test").Result() fmt.Println(ret) } 输出: Connection Redis Client Success! [xxx 33 123456789111] ``` >HKeys 获取指定键对应的hash对象所有的key值 ``` func main() { if err := initRdb();err != nil{ panic(err) } fmt.Println("Connection Redis Client Success!") defer Rdb.Close() ret,_ := Rdb.HKeys("hash_test").Result() fmt.Println(ret) } 输出: Connection Redis Client Success! [name age tel] ``` >HIncrBy key field increValue 增加某个key的value值,increValue表示增加多少,这个值可以为负数 ``` func main() { if err := initRdb();err != nil{ panic(err) } fmt.Println("Connection Redis Client Success!") defer Rdb.Close() ret,_ := Rdb.HGetAll("hash_test").Result() fmt.Println(ret) Rdb.HIncrBy("hash_test","age",10) ret,_ = Rdb.HGetAll("hash_test").Result() fmt.Println(ret) Rdb.HIncrBy("hash_test","age",-20) ret,_ = Rdb.HGetAll("hash_test").Result() fmt.Println(ret) } 输出: Connection Redis Client Success! map[age:43 name:xxx tel:123456789111] map[age:53 name:xxx tel:123456789111] map[age:33 name:xxx tel:123456789111] ``` ### set集合 >SAdd 添加 ``` func main() { if err := initRdb();err != nil{ panic(err) } fmt.Println("Connection Redis Client Success!") defer Rdb.Close() Rdb.SAdd("set_test",11,22,33,44).Result() } ``` >SRem 删除集合中对应的元素 ``` func main() { if err := initRdb();err != nil{ panic(err) } fmt.Println("Connection Redis Client Success!") defer Rdb.Close() Rdb.SRem("set_test",22,44) } ``` > SMembers 获取集合中所有的元素 ``` func main() { if err := initRdb();err != nil{ panic(err) } fmt.Println("Connection Redis Client Success!") defer Rdb.Close() ret,_ := Rdb.SMembers("set_test").Result() fmt.Println(ret) } ``` >SIsMember 判断集合内是否有指定元素 ``` func main() { if err := initRdb();err != nil{ panic(err) } fmt.Println("Connection Redis Client Success!") defer Rdb.Close() ret,_ := Rdb.SIsMember("set_test",22).Result() fmt.Println(ret) ret,_ = Rdb.SIsMember("set_test",33).Result() fmt.Println(ret) } ``` >SRandMember 随机获取集合内的一个元素 ``` func main() { if err := initRdb();err != nil{ panic(err) } fmt.Println("Connection Redis Client Success!") defer Rdb.Close() ret,_ := Rdb.SRandMember("set_test").Result() fmt.Println(ret) } ``` >SRandMemberN 随机获取集群内的n个元素 ``` func main() { if err := initRdb();err != nil{ panic(err) } fmt.Println("Connection Redis Client Success!") defer Rdb.Close() ret,_ := Rdb.SRandMemberN("set_test",2).Result() fmt.Println(ret) } ``` >SPop从集合中随机弹出元素(会破坏结构) ``` func main() { if err := initRdb();err != nil{ panic(err) } fmt.Println("Connection Redis Client Success!") defer Rdb.Close() ret,_ := Rdb.SMembers("set_test").Result() fmt.Println(ret) Rdb.SPop("set_test") ret,_ = Rdb.SMembers("set_test").Result() fmt.Println(ret) } 输出 [11 22 33 44] [11 33 44] ``` >SCard 获取集合内元素的个数 ``` func main() { if err := initRdb();err != nil{ panic(err) } fmt.Println("Connection Redis Client Success!") defer Rdb.Close() ret,_ := Rdb.SCard("set_test").Result() fmt.Println(ret) } ``` >SInter 获取所有集合的交集 ``` func main() { if err := initRdb();err != nil{ panic(err) } fmt.Println("Connection Redis Client Success!") defer Rdb.Close() ret,_ := Rdb.SMembers("set_test").Result() fmt.Println(ret) Rdb.SAdd("set_test1",22,44,55,77) ret,_ = Rdb.SInter("set_test","set_test1").Result() fmt.Println(ret) } 输出: Connection Redis Client Success! [11 33 44] [44] ``` >SDiff 获取所有集合的差集 ``` func main() { if err := initRdb();err != nil{ panic(err) } fmt.Println("Connection Redis Client Success!") defer Rdb.Close() ret,_ := Rdb.SMembers("set_test").Result() fmt.Println(ret) ret,_ = Rdb.SMembers("set_test1").Result() fmt.Println(ret) ret,_ = Rdb.SDiff("set_test","set_test1").Result() fmt.Println(ret) } 输出: Connection Redis Client Success! [11 33 44] [22 44 55 77] [11 33] ``` >SUnion 获取所有集合的并集 ``` func main() { if err := initRdb();err != nil{ panic(err) } fmt.Println("Connection Redis Client Success!") defer Rdb.Close() ret,_ := Rdb.SMembers("set_test").Result() fmt.Println(ret) ret,_ = Rdb.SMembers("set_test1").Result() fmt.Println(ret) ret,_ = Rdb.SUnion("set_test","set_test1").Result() fmt.Println(ret) } 输出 Connection Redis Client Success! [11 33 44] [22 44 55 77] [11 22 33 44 55 77] ``` ## Redis Pipeline > Pipeline管道技术,指的是客户端允许将多个请求一次发给服务器,过程中而不需要等待请求的回复,在最后再一并读取结果即可 ``` func main() { if err := initRdb();err != nil{ panic(err) } fmt.Println("Connection Redis Client Success!") defer Rdb.Close() pipe := Rdb.Pipeline() pipe.Set("key1","value1",0) pipe.Set("key2","value2",0) pipe.Set("key3","value3",0) pipe.Set("key4","value4",0) pipe.Get("key1") cmder,err := pipe.Exec() if err != nil{ fmt.Printf("pipeline exec failed err %v\n",err) return } for _,cmd := range cmder{ fmt.Printf("Pipe Exec: [%v]\n",cmd) } } 输出: Connection Redis Client Success! Pipe Exec: [set key1 value1: OK] Pipe Exec: [set key2 value2: OK] Pipe Exec: [set key3 value3: OK] Pipe Exec: [set key4 value4: OK] Pipe Exec: [get key1: value1] ``` ## Redis事务操作 ### TXPipeline - Redis是单线程,因此单个命令始终是原子的,但是来自不同客户端的两个命令可以依次执行,但是`Multi/exec`能够确保在`Multi/exec`两个语句之间的命令没有其他客户端正在执行命令。基于这样的场景下我们需要使用TxPipeline来解决 - `TxPipeline`类似于Pipeline的方式,不过TxPipeline内部会使用Multi/exec封装排列的命令 ``` func main() { if err := initRdb();err != nil{ panic(err) } fmt.Println("Connection Redis Client Success!") defer Rdb.Close() pipe := Rdb.TxPipeline() pipe.Set("key1","value1",0) pipe.Set("key2","value2",0) pipe.Set("key3","value3",0) pipe.Set("key4","value4",0) pipe.Get("key1") cmder,err := pipe.Exec() if err != nil{ fmt.Printf("pipeline exec failed err %v\n",err) return } for _,cmd := range cmder{ fmt.Printf("Pipe Exec: [%v]\n",cmd) } } 输出 Connection Redis Client Success! Pipe Exec: [set key1 value1: OK] Pipe Exec: [set key2 value2: OK] Pipe Exec: [set key3 value3: OK] Pipe Exec: [set key4 value4: OK] Pipe Exec: [get key1: value1] ``` ## WATCH - 某些场景下,我们除了要使用`Multi/Exec`命令之外,还需要配合`WATCH`命令使用 - 如: 在使用 `WATCH` 命令监视某个键之后, 直到执行 `Exec` 命令的这段时间内, 如果有其他用户抢先对被监视的键进行了替换、更新、删除等操作, 那么当用户尝试执行`Exec` 的时候, 事务将返回一个错误, 用户可以根据这个错误选择重试事务或者放弃事务。 ``` func main() { if err := initRdb();err != nil{ panic(err) } fmt.Println("Connection Redis Client Success!") defer Rdb.Close() key := "watch_count" err := Rdb.Watch(func(tx *redis.Tx) error { n,err := tx.Get(key).Int() if err != nil && err != redis.Nil{ return err } _,err = tx.Pipelined(func(pipe redis.Pipeliner) error{ pipe.Set(key,n+1,0) return nil }) return err },key) if err != nil{ fmt.Printf("WATCH failed error %v\n",err) return } fmt.Printf("Watch get watch_count: %v\n",Rdb.Get(key).Val()) } ```