Redis介绍
Redis是一个开源的内存数据结构存储,常用作数据库、缓存和消息代理。目前它支持的数据结构有诸如string、hash、list、set、zset、bitmap、hyperloglog、geospatial index和stream。Redis内置了复制、Lua脚本、LRU清除、事务和不同级别的磁盘持久性,并通过Redis Sentinel提供高可用性,通过Redis Cluster自动分区。
go-redis库
安装
区别于另一个比较常用的Go语言redis client库:redigo,我们这里采用https://github.com/go-redis/redis连接Redis数据库并进行操作,因为go-redis支持连接哨兵及集群模式的Redis。
使用以下命令下载并安装:
go get -u github.com/go-redis/redis
连接
普通连接
1// 声明一个全局的rdb变量 2var rdb *redis.Client 3 4// 初始化连接 5func initClient() (err error) { 6 // 通过 redis.NewClient 函数即可创建一个 redis 客户端, 这个方法接收一个 redis.Options 对象参数, 通过这个参数, 我们可以配置 redis 相关的属性, 例如 redis 服务器地址, 数据库名, 数据库密码等。 7 rdb = redis.NewClient(&redis.Options{ 8 Addr: "localhost:6379", 9 Password: "", // no password set 10 DB: 0, // use default DB 11 }) 12 // 通过 cient.Ping() 来检查是否成功连接到了 redis 服务器 13 _, err = rdb.Ping().Result() 14 if err != nil { 15 return err 16 } 17 return nil 18}
连接Redis哨兵模式
1func initClient()(err error){ 2 rdb := redis.NewFailoverClient(&redis.FailoverOptions{ 3 MasterName: "master", 4 SentinelAddrs: []string{"x.x.x.x:26379", "xx.xx.xx.xx:26379", "xxx.xxx.xxx.xxx:26379"}, 5 }) 6 _, err = rdb.Ping().Result() 7 if err != nil { 8 return err 9 } 10 return nil 11}
连接Redis集群
1func initClient()(err error){ 2 rdb := redis.NewClusterClient(&redis.ClusterOptions{ 3 Addrs: []string{":7000", ":7001", ":7002", ":7003", ":7004", ":7005"}, 4 }) 5 _, err = rdb.Ping().Result() 6 if err != nil { 7 return err 8 } 9 return nil 10}
连接池
go-redis 已经实现了 redis 的连接池管理, 因此我们不需要自己手动管理 redis 的连接。
默认情况下,连接池大小是10, 可以通过 redis.Options 的 PoolSize 属性, 我们设置了 redis 连接池的大小为5。
1func GetRedisClientPool() *Client{ 2 redisdb := NewClient(&Options{ 3 Addr: "127.0.0.1:6379", 4 Password: "", 5 DB: 0, 6 PoolSize: 5,}) 7 8 pong, err := redisdb.Ping().Result() 9 if err != nil { 10 fmt.Println(pong, err) 11 } 12 return redisdb 13} 14 15// 连接池测试 16func connectPoolTest() { 17 fmt.Println("-----------------------welcome to connect Pool Test-----------------------") 18 client :=GetRedisClientPool() 19 wg := sync.WaitGroup{} 20 wg.Add(10) 21 22 for i := 0; i < 10; i++ { 23 go func() { 24 defer wg.Done() 25 26 for j := 0; j < 1000; j++ { 27 client.Set(fmt.Sprintf("name%d", j), fmt.Sprintf("xys%d", j), 0).Err() 28 client.Get(fmt.Sprintf("name%d", j)).Result() 29 } 30 31 fmt.Printf("PoolStats, TotalConns: %d, IdleConns: %d\n", client.PoolStats().TotalConns, client.PoolStats().IdleConns); 32 }() 33 } 34 35 wg.Wait() 36}
基本使用
String 操作
- Set(key, value):给数据库中名称为key的string赋予值valueget(key):返回数据库中名称为key的string的value
- GetSet(key, value):给名称为key的string赋予上一次的value
- MGet(key1, key2,…, key N):返回库中多个string的value
- SetNX(key, value):添加string,名称为key,值为value
- SetXX(key, time, value):向库中添加string,设定过期时间time
- MSet(key N, value N):批量设置多个string的值
- MSetNX(key N, value N):如果所有名称为key i的string都不存在
- Incr(key):名称为key的string增1操作
- Incrby(key, integer):名称为key的string增加integer
- Decr(key):名称为key的string减1操作
- Decrby(key, integer):名称为key的string减少integer
- Append(key, value):名称为key的string的值附加valuesubstr(key, start, end):返回名称为key的string的value的子串
1func redisExample() { 2 err := rdb.Set("score", 100, 0).Err() 3 if err != nil { 4 fmt.Printf("set score failed, err:%v\n", err) 5 return 6 } 7 8 val, err := rdb.Get("score").Result() 9 if err != nil { 10 fmt.Printf("get score failed, err:%v\n", err) 11 return 12 } 13 fmt.Println("score", val) 14 15 val2, err := rdb.Get("name").Result() 16 if err == redis.Nil { 17 fmt.Println("name does not exist") 18 } else if err != nil { 19 fmt.Printf("get name failed, err:%v\n", err) 20 return 21 } else { 22 fmt.Println("name", val2) 23 } 24}
1func StringDemo() { 2 fmt.Println("-----------------------welcome to StringDemo-----------------------") 3 redisClient:=GetRedisClient() 4 if redisClient ==nil{ 5 fmt.Errorf("StringDemo redisClient is nil") 6 return 7 } 8 9 name := "张三" 10 key :="name:zhangsan" 11 redisClient.Set(key , name,1 * time.Second) 12 val := redisClient.Get(key) 13 if val == nil { 14 fmt.Errorf("StringDemo get error") 15 } 16 fmt.Println("name", val) 17}
List 操作
- RPush(key, value):在名称为key的list尾添加一个值为value的元素
- LPush(key, value):在名称为key的list头添加一个值为value的元素
- LLen(key):返回名称为key的list的长度
- LRange(key, start, end):返回名称为key的list中start至end之间的元素
- LTrim(key, start, end):截取名称为key的list
- LIndex(key, index):返回名称为key的list中index位置的元素
- LSet(key, index, value):给名称为key的list中index位置的元素赋值
- LRem(key, count, value):删除count个key的list中值为value的元素
- LPop(key):返回并删除名称为key的list中的首元素
- RPop(key):返回并删除名称为key的list中的尾元素
- BLPop(key1, key2,… key N, timeout):lpop命令的block版本。
- BRPop(key1, key2,… key N, timeout):rpop的block版本。
- RPopLPush(srckey, dstkey):返回并删除名称为srckey的list的尾元素,并将该元素添加到名称为dstkey的list的头部
1func ListDemo(){ 2 fmt.Println("-----------------------welcome to ListDemo-----------------------") 3 redisClient:=GetRedisClient() 4 if redisClient == nil { 5 fmt.Errorf("ListDemo redisClient is nil") 6 return 7 } 8 articleKey := "article" 9 result,err:=redisClient.RPush(articleKey, "a","b","c").Result() // 10 if err!=nil { 11 fmt.Println(err) 12 return 13 } 14 fmt.Println("result:",result) 15 16 result,err = redisClient.LPush(articleKey, "d").Result() // 17 if err!=nil { 18 fmt.Println(err) 19 return 20 } 21 fmt.Println("result:",result) 22 23 length, err := redisClient.LLen(articleKey).Result() 24 if err != nil { 25 fmt.Println("ListDemo LLen is nil") 26 } 27 fmt.Println("length: ", length) // 长度 28 29 mapOut,err1:=redisClient.LRange(articleKey,0,100).Result() 30 if err1!=nil { 31 fmt.Println(err1) 32 return 33 } 34 for inx, item := range mapOut { 35 fmt.Printf("\n %s:%s", inx, item) 36 } 37}
Hash操作
- HSet(key, field, value):向名称为key的hash中添加元素field
- HGet(key, field):返回名称为key的hash中field对应的value
- HMget(key, (fields)):返回名称为key的hash中field i对应的value
- HMset(key, (fields)):向名称为key的hash中添加元素field
- HIncrby(key, field, integer):将名称为key的hash中field的value增加integer
- HExists(key, field):名称为key的hash中是否存在键为field的域
- HDel(key, field):删除名称为key的hash中键为field的域
- HLen(key):返回名称为key的hash中元素个数
- HKeys(key):返回名称为key的hash中所有键
- HVals(key):返回名称为key的hash中所有键对应的value
- HGetall(key):返回名称为key的hash中所有的键(field)及其对应的value
1func HashDemo() { 2 fmt.Println("-----------------------welcome to HashDemo-----------------------") 3 redisClient := GetRedisClient() 4 if redisClient == nil { 5 fmt.Errorf("HashDemo redisClient is nil") 6 return 7 } 8 article := Article{18, "测试文章内容22222", "测试文章内容22222测试文章内容22222测试文章内容22222", 10, 0} 9 articleKey := "article:18" 10 11 redisClient.HMSet(articleKey, ToStringDictionary(&article)) 12 mapOut := redisClient.HGetAll(articleKey).Val() 13 for inx, item := range mapOut { 14 fmt.Printf("\n %s:%s", inx, item) 15 } 16 fmt.Print("\n") 17 18 redisClient.HSet(articleKey, "Content", "测试文章内容") 19 mapOut = redisClient.HGetAll(articleKey).Val() 20 for inx, item := range mapOut { 21 fmt.Printf("\n %s:%s", inx, item) 22 } 23 fmt.Print("\n") 24 25 view, err := redisClient.HIncrBy(articleKey, "Views", 1).Result() 26 if err != nil { 27 fmt.Printf("\n HIncrBy error=%s ", err) 28 } else { 29 fmt.Printf("\n HIncrBy Views=%d ", view) 30 } 31 fmt.Print("\n") 32 33 mapOut = redisClient.HGetAll(articleKey).Val() 34 for inx, item := range mapOut { 35 fmt.Printf("\n %s:%s", inx, item) 36 } 37 fmt.Print("\n") 38}
zset示例
1func redisExample2() { 2 zsetKey := "language_rank" 3 languages := []*redis.Z{ 4 &redis.Z{Score: 90.0, Member: "Golang"}, 5 &redis.Z{Score: 98.0, Member: "Java"}, 6 &redis.Z{Score: 95.0, Member: "Python"}, 7 &redis.Z{Score: 97.0, Member: "JavaScript"}, 8 &redis.Z{Score: 99.0, Member: "C/C++"}, 9 } 10 // ZADD 11 num, err := rdb.ZAdd(zsetKey, languages...).Result() 12 if err != nil { 13 fmt.Printf("zadd failed, err:%v\n", err) 14 return 15 } 16 fmt.Printf("zadd %d succ.\n", num) 17 18 // 把Golang的分数加10 19 newScore, err := rdb.ZIncrBy(zsetKey, 10.0, "Golang").Result() 20 if err != nil { 21 fmt.Printf("zincrby failed, err:%v\n", err) 22 return 23 } 24 fmt.Printf("Golang's score is %f now.\n", newScore) 25 26 // 取分数最高的3个 27 ret, err := rdb.ZRevRangeWithScores(zsetKey, 0, 2).Result() 28 if err != nil { 29 fmt.Printf("zrevrange failed, err:%v\n", err) 30 return 31 } 32 for _, z := range ret { 33 fmt.Println(z.Member, z.Score) 34 } 35 36 // 取95~100分的 37 op := &redis.ZRangeBy{ 38 Min: "95", 39 Max: "100", 40 } 41 ret, err = rdb.ZRangeByScoreWithScores(zsetKey, op).Result() 42 if err != nil { 43 fmt.Printf("zrangebyscore failed, err:%v\n", err) 44 return 45 } 46 for _, z := range ret { 47 fmt.Println(z.Member, z.Score) 48 } 49} 50 51// 输出结果如下: 52$ ./06redis_demo 53zadd 0 succ. 54Golang's score is 100.000000 now. 55Golang 100 56C/C++ 99 57Java 98 58JavaScript 97 59Java 98 60C/C++ 99 61Golang 100
完整代码
1package main 2 3import ( 4 "fmt" 5 . "github.com/go-redis/redis" 6 . "redisDemo/models" 7 "time" 8 "sync" 9) 10 11func main() { 12 fmt.Println("-----------------------welcome to redisdemo-----------------------") 13 //StringDemo() 14 //ListDemo() 15 //HashDemo() 16 connectPoolTest() 17} 18 19func StringDemo() { 20 fmt.Println("-----------------------welcome to StringDemo-----------------------") 21 redisClient:=GetRedisClient() 22 if redisClient ==nil{ 23 fmt.Errorf("StringDemo redisClient is nil") 24 return 25 } 26 27 name := "张三" 28 key :="name:zhangsan" 29 redisClient.Set(key , name,1 * time.Second) 30 val := redisClient.Get(key) 31 if val == nil { 32 fmt.Errorf("StringDemo get error") 33 } 34 fmt.Println("name", val) 35} 36 37func ListDemo(){ 38 fmt.Println("-----------------------welcome to ListDemo-----------------------") 39 redisClient:=GetRedisClient() 40 if redisClient == nil { 41 fmt.Errorf("ListDemo redisClient is nil") 42 return 43 } 44 articleKey := "article" 45 result,err:=redisClient.RPush(articleKey, "a","b","c").Result() //在名称为 key 的list尾添加一个值为value的元素 46 if err!=nil { 47 fmt.Println(err) 48 return 49 } 50 fmt.Println("result:",result) 51 52 result,err = redisClient.LPush(articleKey, "d").Result() //在名称为 key 的list头添加一个值为value的元素 53 if err!=nil { 54 fmt.Println(err) 55 return 56 } 57 fmt.Println("result:",result) 58 59 length, err := redisClient.LLen(articleKey).Result() 60 if err != nil { 61 fmt.Println("ListDemo LLen is nil") 62 } 63 fmt.Println("length: ", length) // 长度 64 65 mapOut,err1:=redisClient.LRange(articleKey,0,100).Result() 66 if err1!=nil { 67 fmt.Println(err1) 68 return 69 } 70 for inx, item := range mapOut { 71 fmt.Printf("\n %s:%s", inx, item) 72 } 73} 74 75func HashDemo() { 76 fmt.Println("-----------------------welcome to HashDemo-----------------------") 77 redisClient := GetRedisClient() 78 if redisClient == nil { 79 fmt.Errorf("HashDemo redisClient is nil") 80 return 81 } 82 article := Article{18, "测试文章内容22222", "测试文章内容22222测试文章内容22222测试文章内容22222", 10, 0} 83 articleKey := "article:18" 84 85 redisClient.HMSet(articleKey, ToStringDictionary(&article)) 86 mapOut := redisClient.HGetAll(articleKey).Val() 87 for inx, item := range mapOut { 88 fmt.Printf("\n %s:%s", inx, item) 89 } 90 fmt.Print("\n") 91 92 redisClient.HSet(articleKey, "Content", "测试文章内容") 93 mapOut = redisClient.HGetAll(articleKey).Val() 94 for inx, item := range mapOut { 95 fmt.Printf("\n %s:%s", inx, item) 96 } 97 fmt.Print("\n") 98 99 view, err := redisClient.HIncrBy(articleKey, "Views", 1).Result() 100 if err != nil { 101 fmt.Printf("\n HIncrBy error=%s ", err) 102 } else { 103 fmt.Printf("\n HIncrBy Views=%d ", view) 104 } 105 fmt.Print("\n") 106 107 mapOut = redisClient.HGetAll(articleKey).Val() 108 for inx, item := range mapOut { 109 fmt.Printf("\n %s:%s", inx, item) 110 } 111 fmt.Print("\n") 112 113} 114 115func GetRedisClient() *Client { 116 redisdb := NewClient(&Options{ 117 Addr: "127.0.0.1:6379", 118 Password: "", // no password set 119 DB: 0, // use default DB 120 }) 121 122 pong, err := redisdb.Ping().Result() 123 if err != nil { 124 fmt.Println(pong, err) 125 } 126 return redisdb 127} 128 129func GetRedisClientPool() *Client{ 130 redisdb := NewClient(&Options{ 131 Addr: "127.0.0.1:6379", 132 Password: "", 133 DB: 0, 134 PoolSize: 5,}) 135 136 pong, err := redisdb.Ping().Result() 137 if err != nil { 138 fmt.Println(pong, err) 139 } 140 return redisdb 141} 142 143// 连接池测试 144func connectPoolTest() { 145 fmt.Println("-----------------------welcome to connect Pool Test-----------------------") 146 client :=GetRedisClientPool() 147 wg := sync.WaitGroup{} 148 wg.Add(10) 149 150 for i := 0; i < 10; i++ { 151 go func() { 152 defer wg.Done() 153 154 for j := 0; j < 1000; j++ { 155 client.Set(fmt.Sprintf("name%d", j), fmt.Sprintf("xys%d", j), 0).Err() 156 client.Get(fmt.Sprintf("name%d", j)).Result() 157 } 158 159 fmt.Printf("PoolStats, TotalConns: %d, IdleConns: %d\n", client.PoolStats().TotalConns, client.PoolStats().IdleConns); 160 }() 161 } 162 wg.Wait() 163}
