安装

使用的是 https://github.com/go-redis/redis
这个 golang 客户端, 因此安装方式如下:go get gopkg.in/redis.v4
接着在代码中导入此包即可:import “gopkg.in/redis.v4”

基本操作

创建客户端

通过 redis.NewClient 函数即可创建一个 redis 客户端, 这个方法接收一个redis.Options 对象参数, 通过这个参数, 我们可以配置 redis 相关的属性, 例如 redis 服务器地址, 数据库名, 数据库密码等. 下面是一个连接的例子:

  1. // 创建 redis 客户端
  2. func createClient() *redis.Client {
  3. client := redis.NewClient(&redis.Options{
  4. Addr: "localhost:6379",
  5. Password: "",
  6. DB: 0,
  7. })
  8. // 通过 cient.Ping() 来检查是否成功连接到了 redis 服务器
  9. pong, err := client.Ping().Result()
  10. fmt.Println(pong, err)
  11. return client

String 操作

redis 的 String 操作有:

  1. set(key, value):给数据库中名称为keystring赋予值value
  2. get(key):返回数据库中名称为keystringvalue
  3. getset(key, value):给名称为keystring赋予上一次的value
  4. mget(key1, key2,…, key N):返回库中多个stringvalue
  5. setnx(key, value):添加string,名称为key,值为value
  6. setex(key, time, value):向库中添加string,设定过期时间time
  7. mset(key N, value N):批量设置多个string的值
  8. msetnx(key N, value N):如果所有名称为key istring都不存在
  9. incr(key):名称为keystring1操作
  10. incrby(key, integer):名称为keystring增加integer
  11. decr(key):名称为keystring1操作
  12. decrby(key, integer):名称为keystring减少integer
  13. append(key, value):名称为keystring的值附加value
  14. substr(key, start, end):返回名称为keystringvalue的子串

在 go-redis 中, 我们可以直接找到对应的操作方法, 直接上代码

  1. // String 操作
  2. func stringOperation(client *redis.Client) {
  3. // 第三个参数是过期时间, 如果是0, 则表示没有过期时间.
  4. err := client.Set("name", "xys", 0).Err()
  5. if err != nil {
  6. panic(err)
  7. }
  8. val, err := client.Get("name").Result()
  9. if err != nil {
  10. panic(err)
  11. }
  12. fmt.Println("name", val)
  13. // 这里设置过期时间.
  14. err = client.Set("age", "20", 1 * time.Second).Err()
  15. if err != nil {
  16. panic(err)
  17. }
  18. client.Incr("age") // 自增
  19. client.Incr("age") // 自增
  20. client.Decr("age") // 自减
  21. val, err = client.Get("age").Result()
  22. if err != nil {
  23. panic(err)
  24. }
  25. fmt.Println("age", val) // age 的值为21
  26. // 因为 key "age" 的过期时间是一秒钟, 因此当一秒后, 此 key 会自动被删除了.
  27. time.Sleep(1 * time.Second)
  28. val, err = client.Get("age").Result()
  29. if err != nil {
  30. // 因为 key "age" 已经过期了, 因此会有一个 redis: nil 的错误.
  31. fmt.Printf("error: %v\n", err)
  32. }
  33. fmt.Println("age", val)
  34. }

list 操作

redis 的 list 操作有:

  1. rpush(key, value):在名称为keylist尾添加一个值为value的元素
  2. lpush(key, value):在名称为keylist头添加一个值为value 元素
  3. llen(key):返回名称为keylist的长度
  4. lrange(key, start, end):返回名称为keyliststartend之间的元素
  5. ltrim(key, start, end):截取名称为keylist
  6. lindex(key, index):返回名称为keylistindex位置的元素
  7. lset(key, index, value):给名称为keylistindex位置的元素赋值
  8. lrem(key, count, value):删除countkeylist中值为value的元素
  9. lpop(key):返回并删除名称为keylist中的首元素
  10. rpop(key):返回并删除名称为keylist中的尾元素
  11. blpop(key1, key2,… key N, timeout):lpop命令的block版本。
  12. brpop(key1, key2,… key N, timeout):rpopblock版本。
  13. rpoplpush(srckey, dstkey):返回并删除名称为srckeylist的尾元素,并将该元素添加到名称为dstkeylist的头部

在 go-redis 中, 我们可以直接找到对应的操作方法, 直接上代码

  1. go-redis 中, 我们可以直接找到对应的操作方法, 直接上代码
  2. // String 操作
  3. func stringOperation(client *redis.Client) {
  4. // 第三个参数是过期时间, 如果是0, 则表示没有过期时间.
  5. err := client.Set("name", "xys", 0).Err()
  6. if err != nil {
  7. panic(err)
  8. }
  9. val, err := client.Get("name").Result()
  10. if err != nil {
  11. panic(err)
  12. }
  13. fmt.Println("name", val)
  14. // 这里设置过期时间.
  15. err = client.Set("age", "20", 1 * time.Second).Err()
  16. if err != nil {
  17. panic(err)
  18. }
  19. client.Incr("age") // 自增
  20. client.Incr("age") // 自增
  21. client.Decr("age") // 自减
  22. val, err = client.Get("age").Result()
  23. if err != nil {
  24. panic(err)
  25. }
  26. fmt.Println("age", val) // age 的值为21
  27. // 因为 key "age" 的过期时间是一秒钟, 因此当一秒后, 此 key 会自动被删除了.
  28. time.Sleep(1 * time.Second)
  29. val, err = client.Get("age").Result()
  30. if err != nil {
  31. // 因为 key "age" 已经过期了, 因此会有一个 redis: nil 的错误.
  32. fmt.Printf("error: %v\n", err)
  33. }
  34. fmt.Println("age", val)
  35. }
  36. list 操作

set 操作

redis 的 set 操作:

  1. sadd(key, member):向名称为keyset中添加元素member
  2. srem(key, member) :删除名称为keyset中的元素member
  3. spop(key) :随机返回并删除名称为keyset中一个元素
  4. smove(srckey, dstkey, member) :移到集合元素
  5. scard(key) :返回名称为keyset的基数
  6. sismember(key, member) member是否是名称为keyset的元素
  7. sinter(key1, key2,…key N) :求交集
  8. sinterstore(dstkey, (keys)) :求交集并将交集保存到dstkey的集合
  9. sunion(key1, (keys)) :求并集
  10. sunionstore(dstkey, (keys)) :求并集并将并集保存到dstkey的集合
  11. sdiff(key1, (keys)) :求差集
  12. sdiffstore(dstkey, (keys)) :求差集并将差集保存到dstkey的集合
  13. smembers(key) :返回名称为keyset的所有元素
  14. srandmember(key) :随机返回名称为keyset的一个元素

接下来是 go-redis 的 set 操作:

  1. // set 操作
  2. func setOperation(client *redis.Client) {
  3. client.SAdd("blacklist", "Obama") // 向 blacklist 中添加元素
  4. client.SAdd("blacklist", "Hillary") // 再次添加
  5. client.SAdd("blacklist", "the Elder") // 添加新元素
  6. client.SAdd("whitelist", "the Elder") // 向 whitelist 添加元素
  7. // 判断元素是否在集合中
  8. isMember, err := client.SIsMember("blacklist", "Bush").Result()
  9. if err != nil {
  10. panic(err)
  11. }
  12. fmt.Println("Is Bush in blacklist: ", isMember)
  13. // 求交集, 即既在黑名单中, 又在白名单中的元素
  14. names, err := client.SInter("blacklist", "whitelist").Result()
  15. if err != nil {
  16. panic(err)
  17. }
  18. // 获取到的元素是 "the Elder"
  19. fmt.Println("Inter result: ", names)
  20. // 获取指定集合的所有元素
  21. all, err := client.SMembers("blacklist").Result()
  22. if err != nil {
  23. panic(err)
  24. }
  25. fmt.Println("All member: ", all)
  26. }

hash 操作

redis 的 hash 操作:

  1. hset(key, field, value):向名称为keyhash中添加元素field
  2. hget(key, field):返回名称为keyhashfield对应的value
  3. hmget(key, (fields)):返回名称为keyhashfield i对应的value
  4. hmset(key, (fields)):向名称为keyhash中添加元素field
  5. hincrby(key, field, integer):将名称为keyhashfieldvalue增加integer
  6. hexists(key, field):名称为keyhash中是否存在键为field的域
  7. hdel(key, field):删除名称为keyhash中键为field的域
  8. hlen(key):返回名称为keyhash中元素个数
  9. hkeys(key):返回名称为keyhash中所有键
  10. hvals(key):返回名称为keyhash中所有键对应的value
  11. hgetall(key):返回名称为keyhash中所有的键(field)及其对应的value

go-redis 中的 hash 操作:

  1. // hash 操作
  2. func hashOperation(client *redis.Client) {
  3. client.HSet("user_xys", "name", "xys"); // 向名称为 user_xys 的 hash 中添加元素 name
  4. client.HSet("user_xys", "age", "18"); // 向名称为 user_xys 的 hash 中添加元素 age
  5. // 批量地向名称为 user_test 的 hash 中添加元素 name 和 age
  6. client.HMSet("user_test", map[string]string{"name": "test", "age":"20"})
  7. // 批量获取名为 user_test 的 hash 中的指定字段的值.
  8. fields, err := client.HMGet("user_test", "name", "age").Result()
  9. if err != nil {
  10. panic(err)
  11. }
  12. fmt.Println("fields in user_test: ", fields)
  13. // 获取名为 user_xys 的 hash 中的字段个数
  14. length, err := client.HLen("user_xys").Result()
  15. if err != nil {
  16. panic(err)
  17. }
  18. fmt.Println("field count in user_xys: ", length) // 字段个数为2
  19. // 删除名为 user_test 的 age 字段
  20. client.HDel("user_test", "age")
  21. age, err := client.HGet("user_test", "age").Result()
  22. if err != nil {
  23. fmt.Printf("Get user_test age error: %v\n", err)
  24. } else {
  25. fmt.Println("user_test age is: ", age) // 字段个数为2
  26. }
  27. }

关于连接池

redis.v4 包实现了 redis 的连接池管理, 因此我们就不需要自己手动管理 redis 的连接了.
默认情况下, redis.v4 的 redis 连接池大小是10, 不过我们可以在初始化 redis 客户端时自行设置连接池的大小, 例如:

  1. client := redis.NewClient(&redis.Options{
  2. Addr: "localhost:6379",
  3. Password: "",
  4. DB: 0,
  5. PoolSize: 5,
  6. })

通过 redis.Options 的 PoolSize 属性, 我们设置了 redis 连接池的大小为5.
那么接下来我们来看一下这个设置有什么效果吧:

  1. // redis.v4 的连接池管理
  2. func connectPool(client *redis.Client) {
  3. wg := sync.WaitGroup{}
  4. wg.Add(10)
  5. for i := 0; i < 10; i++ {
  6. go func() {
  7. defer wg.Done()
  8. for j := 0; j < 100; j++ {
  9. client.Set(fmt.Sprintf("name%d", j), fmt.Sprintf("xys%d", j), 0).Err()
  10. client.Get(fmt.Sprintf("name%d", j)).Result()
  11. }
  12. fmt.Printf("PoolStats, TotalConns: %d, FreeConns: %d\n", client.PoolStats().TotalConns, client.PoolStats().FreeConns);
  13. }()
  14. }
  15. wg.Wait()
  16. }

上面的例子启动了10个 routine 来不断向 redis 读写数据, 然后我们通过 client.PoolStats() 获取连接池的信息. 运行这个例子, 输出如下:

  1. PoolStats, TotalConns: 5, FreeConns: 1
  2. PoolStats, TotalConns: 5, FreeConns: 1
  3. PoolStats, TotalConns: 5, FreeConns: 1
  4. PoolStats, TotalConns: 5, FreeConns: 1
  5. PoolStats, TotalConns: 5, FreeConns: 1
  6. PoolStats, TotalConns: 5, FreeConns: 2
  7. PoolStats, TotalConns: 5, FreeConns: 2
  8. PoolStats, TotalConns: 5, FreeConns: 3
  9. PoolStats, TotalConns: 5, FreeConns: 4
  10. PoolStats, TotalConns: 5, FreeConns: 5

通过输出可以看到, 此时最大的连接池数量确实是 5 了, 并且一开始时, 因为 coroutine 的数量大于5, 会造成 redis 连接不足的情况(反映在 FreeConns 上就是前几次的输出 FreeConns 一直是1), 当某个 coroutine 结束后, 会释放此 redis 连接, 因此 FreeConns 会增加.

完整示例

  1. //
  2. // author xiongyongshun
  3. // project go_redis
  4. // version 1.0
  5. // created 16/10/6 03:49
  6. //
  7. package main
  8. import (
  9. "fmt"
  10. "gopkg.in/redis.v4"
  11. "time"
  12. "sync"
  13. )
  14. func main() {
  15. client := createClient()
  16. defer client.Close()
  17. stringOperation(client)
  18. listOperation(client)
  19. setOperation(client)
  20. hashOperation(client)
  21. connectPool(client)
  22. }
  23. // 创建 redis 客户端
  24. func createClient() *redis.Client {
  25. client := redis.NewClient(&redis.Options{
  26. Addr: "localhost:6379",
  27. Password: "",
  28. DB: 0,
  29. PoolSize: 5,
  30. })
  31. pong, err := client.Ping().Result()
  32. fmt.Println(pong, err)
  33. return client
  34. }
  35. // String 操作
  36. func stringOperation(client *redis.Client) {
  37. // 第三个参数是过期时间, 如果是0, 则表示没有过期时间.
  38. err := client.Set("name", "xys", 0).Err()
  39. if err != nil {
  40. panic(err)
  41. }
  42. val, err := client.Get("name").Result()
  43. if err != nil {
  44. panic(err)
  45. }
  46. fmt.Println("name", val)
  47. // 这里设置过期时间.
  48. err = client.Set("age", "20", 1 * time.Second).Err()
  49. if err != nil {
  50. panic(err)
  51. }
  52. client.Incr("age") // 自增
  53. client.Incr("age") // 自增
  54. client.Decr("age") // 自减
  55. val, err = client.Get("age").Result()
  56. if err != nil {
  57. panic(err)
  58. }
  59. fmt.Println("age", val) // age 的值为21
  60. // 因为 key "age" 的过期时间是一秒钟, 因此当一秒后, 此 key 会自动被删除了.
  61. time.Sleep(1 * time.Second)
  62. val, err = client.Get("age").Result()
  63. if err != nil {
  64. // 因为 key "age" 已经过期了, 因此会有一个 redis: nil 的错误.
  65. fmt.Printf("error: %v\n", err)
  66. }
  67. fmt.Println("age", val)
  68. }
  69. // list 操作
  70. func listOperation(client *redis.Client) {
  71. client.RPush("fruit", "apple") //在名称为 fruit 的list尾添加一个值为value的元素
  72. client.LPush("fruit", "banana") //在名称为 fruit 的list头添加一个值为value的 元素
  73. length, err := client.LLen("fruit").Result() //返回名称为 fruit 的list的长度
  74. if err != nil {
  75. panic(err)
  76. }
  77. fmt.Println("length: ", length) // 长度为2
  78. value, err := client.LPop("fruit").Result() //返回并删除名称为 fruit 的list中的首元素
  79. if err != nil {
  80. panic(err)
  81. }
  82. fmt.Println("fruit: ", value)
  83. value, err = client.RPop("fruit").Result() // 返回并删除名称为 fruit 的list中的尾元素
  84. if err != nil {
  85. panic(err)
  86. }
  87. fmt.Println("fruit: ", value)
  88. }
  89. // set 操作
  90. func setOperation(client *redis.Client) {
  91. client.SAdd("blacklist", "Obama") // 向 blacklist 中添加元素
  92. client.SAdd("blacklist", "Hillary") // 再次添加
  93. client.SAdd("blacklist", "the Elder") // 添加新元素
  94. client.SAdd("whitelist", "the Elder") // 向 whitelist 添加元素
  95. // 判断元素是否在集合中
  96. isMember, err := client.SIsMember("blacklist", "Bush").Result()
  97. if err != nil {
  98. panic(err)
  99. }
  100. fmt.Println("Is Bush in blacklist: ", isMember)
  101. // 求交集, 即既在黑名单中, 又在白名单中的元素
  102. names, err := client.SInter("blacklist", "whitelist").Result()
  103. if err != nil {
  104. panic(err)
  105. }
  106. // 获取到的元素是 "the Elder"
  107. fmt.Println("Inter result: ", names)
  108. // 获取指定集合的所有元素
  109. all, err := client.SMembers("blacklist").Result()
  110. if err != nil {
  111. panic(err)
  112. }
  113. fmt.Println("All member: ", all)
  114. }
  115. // hash 操作
  116. func hashOperation(client *redis.Client) {
  117. client.HSet("user_xys", "name", "xys"); // 向名称为 user_xys 的 hash 中添加元素 name
  118. client.HSet("user_xys", "age", "18"); // 向名称为 user_xys 的 hash 中添加元素 age
  119. // 批量地向名称为 user_test 的 hash 中添加元素 name 和 age
  120. client.HMSet("user_test", map[string]string{"name": "test", "age":"20"})
  121. // 批量获取名为 user_test 的 hash 中的指定字段的值.
  122. fields, err := client.HMGet("user_test", "name", "age").Result()
  123. if err != nil {
  124. panic(err)
  125. }
  126. fmt.Println("fields in user_test: ", fields)
  127. // 获取名为 user_xys 的 hash 中的字段个数
  128. length, err := client.HLen("user_xys").Result()
  129. if err != nil {
  130. panic(err)
  131. }
  132. fmt.Println("field count in user_xys: ", length) // 字段个数为2
  133. // 删除名为 user_test 的 age 字段
  134. client.HDel("user_test", "age")
  135. age, err := client.HGet("user_test", "age").Result()
  136. if err != nil {
  137. fmt.Printf("Get user_test age error: %v\n", err)
  138. } else {
  139. fmt.Println("user_test age is: ", age) // 字段个数为2
  140. }
  141. }
  142. // redis.v4 的连接池管理
  143. func connectPool(client *redis.Client) {
  144. wg := sync.WaitGroup{}
  145. wg.Add(10)
  146. for i := 0; i < 10; i++ {
  147. go func() {
  148. defer wg.Done()
  149. for j := 0; j < 100; j++ {
  150. client.Set(fmt.Sprintf("name%d", j), fmt.Sprintf("xys%d", j), 0).Err()
  151. client.Get(fmt.Sprintf("name%d", j)).Result()
  152. }
  153. fmt.Printf("PoolStats, TotalConns: %d, FreeConns: %d\n", client.PoolStats().TotalConns, client.PoolStats().FreeConns);
  154. }()
  155. }
  156. wg.Wait()
  157. }