Go语言下Redis 实战学习| 青训营

1,251 阅读3分钟

Redis作为一种在后端系统中运用广泛的中间件,除了普通的Kv键值数据库功能,还有其他高级用法值得探索

初识Redis

我学习Redis的初步认识就是一个集中式的KV键值对数据库,具有很高的读写性能。可以适用于多个服务端的统一授权验证,存储一些自动过期的键值,性能也非常的高。 也学习到一些Redis更擅长的使用场景

  • 高性能集中缓存系统
  • 网络访问量转发量评论量的计数器
  • 实时更新的排行榜 有序集合
  • 消息队列 发布订阅实现或者是阻塞队列

Redis高级功能

Redis的常见数据结构像List Set ZSet String Hash 标准的KV再继续简单介绍的话篇幅就过于长了,打算后续再对这个5种数据结构深入了解。

Pipeline

在不同语言的Redis 客户端下都会对实现Pipeline(管道) 功能, 主要就是独占一条链接将要执行的数据一致发送而不断开,我的第一感觉有点像Http的Keep-Alive,通过缩短打开和关闭的网络时间从而提高效率

我觉得还是又要注意得地方相比于带M得指令比如Mset,Hmset等,这种指令是原生就是批量执行会原子执行,而Pipeline虽然是统一发送但是在Redis-Server上则是拆开来一条一条得执行

GO下实例 初始化

    import "github.com/redis/go-redis/v9"

    var (
        IRB *redis.Client
    )

    func init() {
        IRB = redis.NewClient(&redis.Options{
           Addr: "localhost:6379",
           DB:   0,
        })
    }

管道goredis实现方式

    func TestPipeline(t *testing.T) {
        pipe := IRB.Pipeline()
        r1 := pipe.Set(ctx, "key1", "32", 0)
        r2 := pipe.Set(ctx, "key2", 64, 0)
        exec, err := pipe.Exec(ctx)
        if err != nil {
           t.Error(exec)
        }
        log.Println(r1.Val())
        log.Println(r2.Val())
    }

如果采取上述方案必须等Pipe执行后才能拿到结果 也可采取回调得形式,避免了手动Exec得调用,但是编程体验也下降了,我还是觉得第一种方式比较符合正常编程思路

func TestPipeCallback(t *testing.T) {
    pipelined, err := IRB.Pipelined(ctx, func(pipeliner redis.Pipeliner) error {
       pipeliner.Set(ctx, "cbKey1", 2, 0)
       pipeliner.Set(ctx, "cbKey2", 2, 0)
       return nil
    })
    if err != nil {
       t.Error(err)
    }
    log.Println(pipelined)
}

执行结果如下

image.png

个人体验上Pipe和Redis的事务的Multi比较像,事务也是在执行队列中统一执行,管道就是省去一部分网络传输的时间,当然Redis事务还有其他命令比如Watch等,功能上更完善也有另外的用途。

Publish-Subscribe

go语言下的go-redis的在发布订阅模式中支持在网络异常中重连服务器

定时发布

func PubTicker(channel string) {
    ticker := time.NewTicker(time.Second * 3)
    for {
       select {
       case <-ticker.C:
          IRB.Publish(ctx, channel, time.Now().Format("02/15-04-05"))
       }
    }
}

接收消息ReceiveMessage

func TestPubSub(t *testing.T) {
    var channel = "chan"

    go PubTicker(channel)

    subscribe := IRB.Subscribe(ctx, channel)
    defer subscribe.Close()
    for {
       message, err := subscribe.ReceiveMessage(ctx)
       if err != nil {
          t.Error(err)
       }
       log.Println(message.String())
    }

}

订阅结束后要记得关闭订阅

	messages := subscribe.Channel()
	for message := range messages {
		log.Println(message)
	}

这样去获取消息也是可取的 for-range会一直遍历到通道关闭 可能会造成死锁 channel for range之前建议close或者使用for - select 循环取出数据

tips: go-redis的详细配置选项见文档 Go Redis [配置]

发布订阅和消息队列的小比较: 发布订阅数全收到,消息队列有个抢的过程,只有一个抢到

BitMap

Redis的Bitmap用于大量数据的统计,但是状态一般只能是两个,状态的转换只需要一个Bit的空间,相比于普通的int内存占用大大降低 可以实现的场景

  • 用户登录统计
  • 统计用户每日的操作数 (需要手动划分字节但是支持的数目可以很大)

总结学习

Redis还有更高级的事务Multi和Lua脚本原子性执行,Geo和HyperLogLog和Streams 实现消息队列 等还有Redis的持久化机制学习RDB和AOF,本篇只是简单介绍一部分高级数据结构和Go语言的实战使用,学不可以已 /(ㄒoㄒ)/~~