Redis介绍

Redis是一个开源的内存数据库,Redis提供了多种不同类型的数据结构,很多业务场景下的问题都可以很自然地映射到这些数据结构上。除此之外,通过复制、持久化和客户端分片等特性,我们可以很方便地将Redis扩展成一个能够包含数百GB数据、每秒处理上百万次请求的系统。

Redis支持的数据结构

Redis支持诸如字符串(strings)、哈希(hashes)、列表(lists)、集合(sets)、带范围查询的排序集合(sorted sets)、位图(bitmaps)、hyperloglogs、带半径查询和流的地理空间索引等数据结构(geospatial indexes)。

Redis应用场景

  • 缓存系统,减轻主数据库(MySQL)的压力。
  • 计数场景,比如微博、抖音中的关注数和粉丝数。
  • 热门排行榜,需要排序的场景特别适合使用ZSET。
  • 利用LIST可以实现队列的功能。

    Go操作Redis

    安装

    区别于另一个比较常用的Go语言redis client库:redigo,我们这里采用https://github.com/go-redis/redis连接Redis数据库并进行操作,因为go-redis支持连接哨兵及集群模式的Redis。
    使用以下命令下载并安装:
    1. go get -u github.com/go-redis/redis

连接

普通连接

  1. // 声明一个全局的rdb变量
  2. var rdb *redis.Client
  3. // 初始化连接
  4. func initClient() (err error) {
  5. rdb = redis.NewClient(&redis.Options{
  6. Addr: "localhost:6379",
  7. Password: "", // no password set
  8. DB: 0, // use default DB
  9. })
  10. _, err = rdb.Ping().Result()
  11. if err != nil {
  12. return err
  13. }
  14. return nil
  15. }

连接哨兵模式

  1. func 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. }

连接集群模式

  1. func 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. }

基本使用

set/get

  1. func 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. val, err := rdb.Get("score").Result()
  8. if err != nil {
  9. fmt.Printf("get score failed, err:%v\n", err)
  10. return
  11. }
  12. fmt.Println("score", val)
  13. val2, err := rdb.Get("name").Result()
  14. if err == redis.Nil {
  15. fmt.Println("name does not exist")
  16. } else if err != nil {
  17. fmt.Printf("get name failed, err:%v\n", err)
  18. return
  19. } else {
  20. fmt.Println("name", val2)
  21. }
  22. }

zset

  1. func 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. // 把Golang的分数加10
  18. newScore, err := rdb.ZIncrBy(zsetKey, 10.0, "Golang").Result()
  19. if err != nil {
  20. fmt.Printf("zincrby failed, err:%v\n", err)
  21. return
  22. }
  23. fmt.Printf("Golang's score is %f now.\n", newScore)
  24. // 取分数最高的3个
  25. ret, err := rdb.ZRevRangeWithScores(zsetKey, 0, 2).Result()
  26. if err != nil {
  27. fmt.Printf("zrevrange failed, err:%v\n", err)
  28. return
  29. }
  30. for _, z := range ret {
  31. fmt.Println(z.Member, z.Score)
  32. }
  33. // 取95~100分的
  34. op := &redis.ZRangeBy{
  35. Min: "95",
  36. Max: "100",
  37. }
  38. ret, err = rdb.ZRangeByScoreWithScores(zsetKey, op).Result()
  39. if err != nil {
  40. fmt.Printf("zrangebyscore failed, err:%v\n", err)
  41. return
  42. }
  43. for _, z := range ret {
  44. fmt.Println(z.Member, z.Score)
  45. }
  46. }

更多文档

另一种操作redis

  1. go get github.com/gomodule/redigo/redis

简单操作

  1. package main
  2. import (
  3. "fmt"
  4. "github.com/gomodule/redigo/redis"
  5. )
  6. func main(){
  7. conn, err := redis.Dial("tcp", "122.51.79.172:6379")
  8. if err != nil {
  9. fmt.Println("redis connect error. err: ",err)
  10. return
  11. }
  12. defer conn.Close()
  13. // 设置值
  14. if _, err := conn.Do("set", "name", "joker");err!=nil{
  15. fmt.Println("set key failed. err:",err)
  16. }
  17. // 获取值
  18. reply, err := conn.Do("get", "name")
  19. if err != nil {
  20. fmt.Println("get key failed. err:",err)
  21. }
  22. // 输出值
  23. s, err := redis.String(reply,err)
  24. if err != nil {
  25. fmt.Println("data format failed. err:",err)
  26. }
  27. fmt.Println(s)
  28. }

说明:连接成功后,所有得操作都通过Do()方法,返回的是一个接口类型,如果要输出其值需要对其进行类型断言。这个库已经封装了需要的类型断言。

连接池

连接池的作用是维护一定数量的连接,我们需要的时候就从连接池中取一个连接进行操作,操作结束后再把它还回去,可以减少客户端每次都需要与服务端连接所造成的性能损耗。

代码如下:

  1. func myRedisPool()*redis.Pool{
  2. return &redis.Pool{
  3. Dial: func() (conn redis.Conn, err error) {
  4. return redis.Dial("tcp","x.x.x.x:6379")
  5. },
  6. TestOnBorrow: nil,
  7. MaxIdle: 0,
  8. MaxActive: 0,
  9. IdleTimeout: 0,
  10. Wait: false,
  11. MaxConnLifetime: 0,
  12. }
  13. }

其中:

  • Dial:初始化连接redis的代码
  • TestOnBorrow:用于测试redis
  • MaxIdle:最大空闲数
  • MaxActive:最大连接数,0表示不限制
  • IdleTimeout:最大空闲时间
  • MaxConnLifetime:最大连接活跃时间

    注意:要从连接池中取数据要保证连接池没有关闭

操作:
(1)、从连接池取连接

  1. pool := myRedisPool()
  2. pool.Get()

(2)、关闭连接

  1. pool.Close()

其他操作。

  1. package main
  2. import (
  3. "encoding/json"
  4. "errors"
  5. "fmt"
  6. "time"
  7. "github.com/garyburd/redigo/redis"
  8. )
  9. const (
  10. RedisURL = "redis://*****:6379"
  11. redisMaxIdle = 3 //最大空闲连接数
  12. redisIdleTimeoutSec = 240 //最大空闲连接时间
  13. RedisPassword = "*****"
  14. )
  15. // NewRedisPool 返回redis连接池
  16. func NewRedisPool(redisURL string) *redis.Pool {
  17. return &redis.Pool{
  18. MaxIdle: redisMaxIdle,
  19. IdleTimeout: redisIdleTimeoutSec * time.Second,
  20. Dial: func() (redis.Conn, error) {
  21. c, err := redis.DialURL(redisURL)
  22. if err != nil {
  23. return nil, fmt.Errorf("redis connection error: %s", err)
  24. }
  25. //验证redis密码
  26. if _, authErr := c.Do("AUTH", RedisPassword); authErr != nil {
  27. return nil, fmt.Errorf("redis auth password error: %s", authErr)
  28. }
  29. return c, err
  30. },
  31. TestOnBorrow: func(c redis.Conn, t time.Time) error {
  32. _, err := c.Do("PING")
  33. if err != nil {
  34. return fmt.Errorf("ping redis error: %s", err)
  35. }
  36. return nil
  37. },
  38. }
  39. }
  40. func set(k, v string) {
  41. c := NewRedisPool(RedisURL).Get()
  42. defer c.Close()
  43. _, err := c.Do("SET", k, v)
  44. if err != nil {
  45. fmt.Println("set error", err.Error())
  46. }
  47. }
  48. func getStringValue(k string) string {
  49. c := NewRedisPool(RedisURL).Get()
  50. defer c.Close()
  51. username, err := redis.String(c.Do("GET", k))
  52. if err != nil {
  53. fmt.Println("Get Error: ", err.Error())
  54. return ""
  55. }
  56. return username
  57. }
  58. func SetKeyExpire(k string, ex int) {
  59. c := NewRedisPool(RedisURL).Get()
  60. defer c.Close()
  61. _, err := c.Do("EXPIRE", k, ex)
  62. if err != nil {
  63. fmt.Println("set error", err.Error())
  64. }
  65. }
  66. func CheckKey(k string) bool {
  67. c := NewRedisPool(RedisURL).Get()
  68. defer c.Close()
  69. exist, err := redis.Bool(c.Do("EXISTS", k))
  70. if err != nil {
  71. fmt.Println(err)
  72. return false
  73. } else {
  74. return exist
  75. }
  76. }
  77. func DelKey(k string) error {
  78. c := NewRedisPool(RedisURL).Get()
  79. defer c.Close()
  80. _, err := c.Do("DEL", k)
  81. if err != nil {
  82. fmt.Println(err)
  83. return err
  84. }
  85. return nil
  86. }
  87. func SetJson(k string, data interface{}) error {
  88. c := NewRedisPool(RedisURL).Get()
  89. defer c.Close()
  90. value, _ := json.Marshal(data)
  91. n, _ := c.Do("SETNX", k, value)
  92. if n != int64(1) {
  93. return errors.New("set failed")
  94. }
  95. return nil
  96. }
  97. func getJsonByte(k string) ([]byte, error) {
  98. jsonGet, err := redis.Bytes(c.Do("GET", key))
  99. if err != nil {
  100. fmt.Println(err)
  101. return nil, err
  102. }
  103. return jsonGet, nil
  104. }