【Go Redis 基础 | 青训营笔记】

92 阅读4分钟

这是我参与【第五届青训营】伴学笔记创作活动的第十三天。redis 作为数据缓冲中起重要作用的工具,在企业应用中起重要作用。今天就写一下 go redis 的基础知识。

image.png

Go Redis 基础

go redis 安装配置

  1. 下载地址:Redis
  2. 安装 go-redis :go get github.com/go-redis/redis/v9

redis 用法

redis 连接及基本使用

import "github.com/ go-redis/redis/v9"
var rdb *redis.Client
func init() {
    // redis 客户端的基本配置
    rdb = redis. NewClient(&redis.options{
        Addr :" localhost:6379" 
        Password: "", // no password set
        DB: 0,		  // use default DB
    })
}
func main() {
    ctx := context.Background()
    
    // 法一
    // key + value + 过期时间 (0 为永久保存)
    err := rdb.Set(ctx, "gokey", "govalue", 0).Err()  // 相当于一个 map 设置值
    if err != nil {
        panic(err)
    }
    val, err := rdb.Get(ctx, "gokey").Result()  // 获得 key 为 gokey 的值
    
    // 法二: 自定义用法
    result, err := rdb.Do(ctx, "get", "gokey", "govalue").Result()  // 获取值为interface{} 类型
    if err != nil {
        panic(err)
    }
    val := result.(valuetype)
}

Redis 基本类型及使用

可以看看文档 Redis五种基本数据类型_redis的五种数据类型_NeverOW的博客-CSDN博客

String 类型

  1. Set,Get 命令

  2. GetSet 命令

    // 返回 gokey 旧值并设定新值为 new value
    oldval, err := rdb.GetSet(ctx, "gokey", "new value").Result()  
    
  3. SetNX 命令

    // 不存在则设置值
    err := rdb.SetNX(ctx, "key1", "value1", 0).Err()
    
  4. MGet 命令

    // 批量查询
    data, err := rdb.MGet(ctx, "key1", "key2", "key3").Result()
    for val := range data {
    }
    
  5. Mset 命令

    // 批量设置值
    // 可用 map
    err := rdb.MSet(ctx, "key1", "value1", "key2", "value2").Err()
    
  6. Incr, IncrBy, Decr, DecrBy 命令

    // 针对一个 key 的数值进行递增的操作
    val, err := rdb.Incr(ctx, "key").Result()
    val, err := rdb.IncrBy(ctx, "key", 2).Result()
    // IncrByFloat 可以设定 float 值
    // 相应的 Decr 类型为递减,且用法和 Incr 一致
    
  7. Del 命令

    err := rdb.Del(ctx, "key1", "key2").Err()  // 删除
    
  8. Expire 命令

    // 设置过期时间
    rdb.Expire(ctx, "key", 10 * time.Second)
    

Hash 类型

  1. HSet 命令

    // hash表 id + 字段名 + 字段值
    err := rdb.HSet(ctx, "user_1", "username", "zhangsan")
    
  2. HGet 命令

    username, err := rdb.HGet(ctx, "user_1", "username")
    
  3. HMSet, HMGet ,HSetNX, HIncr 等同 String 一样

  4. HKeys 命令

    keys, err := rdb.Hkeys(ctx, "user_1").Result()  // 返回所有字段名
    
  5. HExists 命令

    exist, err := rdb.HExists(ctx, "user_1", "username").Result()  // 判断是否存在
    

List 类型

  1. LPush,RPush 命令

    rdb.LPush(ctx, "key", 1, 2, 3, 4)  // 从列表左边插入数据
    
  2. RPop, LPop 命令

    val, err := rdb.RPop(ctx, "key").Result() //从列表右边删除一个数据,并返回该数据
    
  3. LRange 命令

    vals, err := rdb.LRange(ctx, "key", 0, 1)  // 取下标为 [0,1] 的数据
    
  4. LRem, LIndex,LInsert 等用法可以看看参考文档

Set 类型

参考 c++ 的 set 类型

Sorted Set 类型

存储自动排序的 set 类型数据

err := rdb.ZAdd(ctx, "key", &redis.Z{Score: 3.7, Member: "zhangsan"}).Err  // 根据分数从小到大排序
vals, err := rdb.ZRange(ctx, "key", 0, -1).Result()
if err != nil {
    panic(err)
}
for _, val := range vals {
    fmt.Println(val)
}

Redis 两个重要功能

发布订阅

Redis提供了发布订阅功能,可以用于消息的传输。Redis的发布订阅机制包括三个部分,发布者,订阅者和Channel。

1.png

发布者和订阅者都是Redis客户端,Channel则为Redis服务器端,发布者将消息发送到某个的频道,订阅了这个频道的订阅者就能接收到这条消息。

  1. Subscribe 函数

    ctx := context.Background()
    sub := rdb.Subscribe(ctx, "channel1")  // 订阅
    
    // 接收消息:法一
    for ch := range sub.Channel() {
        fmt.Println(ch.Channel)  // 频道名称
        fmt.Println(ch.PayLoad)  // 收到的内容
    }
    // 法二
    for {
        message, err := sub.RecieveMessage(ctx)
        if err != nil {
            panic(err)
        }
        fmt.Println(ch.Channel)  // 频道名称
        fmt.Println(ch.PayLoad)  // 收到的内容
    }
    
  2. Publish 函数

    ctx := context.Background()
    rdb.Publish(ctx, "channel1", "msg")  // 发步消息
    
  3. PSubscribe 函数

    // 支持格式订阅
    sub := rdb.PSubscribe(ctx, "channel_*") 
    
  4. Unsubscribe 函数: 取消订阅

  5. PubSubNumSub 函数

    ctx := context.Background()
    chs, _ := rdb.PubSubNumSub(ctx, "channel_1").Result()
    for ch, count := range chs {
        fmt.Println(ch)  // channel 名字
        fmt.Println(count)  // 订阅者数量
    }
    

事务处理

redis事务可以一次执行多个命令,并且带有以下两个重要的性质:

  1. 事务是一个单独的隔离操作:务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
  2. 事务是一个原子操作:事务中的命令要么全部被执行,要么全部都不执行。

TxPipeline

pipe := rdb.TxPipeline()  // 开启一个事务

// 执行事务操作, 可以通过 pipe 读写 redis
ctx := context.Background()
chs, _ := rdb.PubSubNumSub(ctx, "channel_1").Result()

_, err := pipe.Exec(ctx)  // 提交事务
fmt.Println(chs.Val(), err)  // 可以查询事务操作过程中的结果

watch

// redis 乐观锁支持,通过 watch 监听一些 Key
ctx := context.Background()
// 定义一个回调函数,用于处理事务逻辑
fn := func(tx *redis.Tx) error{
    // 先查询一下当前 watch 监听的 key 的值
    v, err := tx.Get(ctx, "key").Int()
    if err != nil && err != redis.Nil {
        return err
    }
    
    // 开始处理业务
    v++
    // 结束处理业务
    
    // 如果 key 的值没有变化, Pipelined 函数才能执行成功
    _, err = tx.Pipelined(ctx, func(pipe redis.Pipeliner) error {
        // 这里给 key 设置新值
        pipe.Set(ctx, "key", v, 0)
        return nil
    })
}
for i := 0; i < 3; i++ {
	err := rdb.Watch(ctx, fn, "key1", "key2")
    if err == nil {
        break
    }
    if err == redis.TxFailedErr {
        continue
    }
}