前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Go(四)Redis还不会使用?

Go(四)Redis还不会使用?

作者头像
lomtom
发布2021-12-10 14:50:31
7430
发布2021-12-10 14:50:31
举报
文章被收录于专栏:博思奥园博思奥园

redis

底层为C语言解决hash冲突类似于1.7的hashmap

redis概念

  1. 非关系型的键值对数据库,可以根据键以O(1)的时间复杂度取出或插入关联值
  2. Reds的数据是存在内存中的
  3. 键值对中键的类型可以是字符串,整型,浮点型等,且键是唯一的
  4. 键值对中的值类型可以是 string,hash,list,set, sorted set等
  5. Reds内置了复制,磁盘持久化,LUA脚本,事务,SSL,客户端代理等功能
  6. 通过Reds哨兵和自动分区提供高可用性

应用场景

  1. 计数器 可以对Sng进行自增自减运算,从而实现计数器功能。Reds这种内存型数据库的读写性能非常高, 很适合存储频繁读写的计数量
  2. 分布式D生成 利用自增特性,一次请求一个大一点的步长如incr2000,缓存在本地使用,用完再请求。
  3. 海量数据统计 位图(btmp):存储是否参过次活动,是否已读某篇文章,用户是否为会员,日活统计。
  4. 会话缓存 可以使用 Redis来统一存储多台应用服务器的会话信息。当应用服务器不再存储用户的会话信息,也就 不再具有状态,一个用户可以请求任意一个应用服务器,从而更容易实现高可用性以及可伸缩性。
  5. 分布式队列/阻塞队列 List是一个双向链表,可以通过 push/push和rpop/pop写入和读取消息。可以通过使用 brpop/b|pop 来实现阻塞队列

String(最大存储512M)

String 是redis中使用最多的存储类型

1、数据结构(3.2之前):sds:simple dynamic string是一个二进制安全数组 sds:

  • length:长度
  • free:剩余长度
  • char [] :{1,12,324,123}

2、数据结构(3.2之后) 根据存储的内容来

3、扩容机制 容量不够时,扩容为原来的两倍,直到1024k,不再成倍增加,而是以1024k的增加。

Go中使用Redis

Go中也有很多比较流行的并且开源Redis库,比如go-redis或redigo,在github上,分别12.3k和8.6k的star数量(截止到2021.09.03)

在这里将以go-redis为例。

安装

第一步:安装go-redis

代码语言:javascript
复制
请勿省略版本号
go get github.com/go-redis/redis/v8

配置连接

第二步:连接Redis服务器 连接Redis服务器有两种方法,第一种使用redis.Options,第二种就是使用redis.ParseURL

代码语言:javascript
复制
1、第一种
import "github.com/go-redis/redis/v8"

rdb := redis.NewClient(&redis.Options{
 Addr:   "localhost:6379",
 Password: "", // no password set
 DB:    0,  // use default DB
})

2、第二种
opt, err := redis.ParseURL("redis://<user>:<pass>@localhost:6379/<db>")
if err != nil {
 panic(err)
}

rdb := redis.NewClient(opt)

那么,我这里使用从我的配置里读取,不会使用go读取配置文件可以参考

Go(三)Go配置文件

代码语言:javascript
复制
import (
 "context"
 "github.com/go-redis/redis/v8"
 "log"
)

var rdb *redis.Client

// GetRedis 获取连接
func GetRedis() *redis.Client {
 return rdb
}

func InitRedis() {
 rdb =  redis.NewClient(&redis.Options{
  Addr:     Redis.Host + ":" + Redis.Port,
  Password: Redis.Password,
  DB:       Redis.DB,
 })
 _,err := rdb.Ping(context.Background()).Result()
 if err != nil{
  log.Printf("redis connect get failed.%v",err)
  return
 }
 log.Printf("redis init success")
}

“Ping() 旧版本是不需要参数的,从v8版本开始 需要参数 context.Context ”

编写存取逻辑

第三步:封装 存/获取值 函数

网上大部分教程都是在v8之前的,而在v8需要传入context.Context,所以在存取时需要额外增加一个参数

代码语言:javascript
复制
1、存值
var ctx = context.Background()

func SetString(key string,value interface{}) error  {
 if key == "" || value == nil {
  return  errors.New(common.ArgsIsNull)
 }
 rdb := config.GetRedis()
 err := rdb.Set(ctx,key,value,0).Err()
 if err != nil {
  return errors.New(err.Error())
 }
 log.Infof("push key: %v, value: %v",key,value)
 return nil
}


2、取值
func GetString(key string) (interface{},error)  {
 if key == "" {
  return  "",errors.New(common.ArgsIsNull)
 }
 rdb := config.GetRedis()
 res,err := rdb.Get(ctx,key).Result()
 if err == redis.Nil{
  return "",errors.New(fmt.Sprintf(common.ResIsNull,key))
 }
 if err != nil {
  return "",errors.New(err.Error())
 }
 return res,nil
}

说明:

  1. Set除了context、键、值外,还需要传入一个过期时间
  2. Get方法,返回错误码可能是redis当中不含有该值,所以做一个特殊处理

使用

第四步:使用

代码语言:javascript
复制
func  Test(test *testing.T)  {
 err := SetString("1", "12")
 if err != nil {
  return
 }
}
func  Test1(test *testing.T)  {
 res,err := GetString("12")
 if err != nil {
  log.Debug(err)
 }else {
  log.Debug(res)
 }
}

把值为12 push到键为1,然后在redis中查看

代码语言:javascript
复制
1、redis控制台
Connecting ...

Connected.
redis:0>get 1
"12"

2、代码获取
=== RUN   Test1
2021-09-03 20:30:38 DEBUG redis/redis_test.go:40 12
--- PASS: Test1 (0.00s)
PASS

Redis 发布/订阅模式

Redis 发布订阅 (pub/sub) 是一种消息通信模式:发送者 (pub) 发送消息,订阅者 (sub) 接收消息。

值得注意的是:订阅者接收不到启动之前的消息。

订阅者

代码语言:javascript
复制
func Receive(key ...string) error {
 if len(key) <= 0 {
  return errors.New(common.ArgsIsNull)
 }
 rdb := config.GetRedis()
 sub := rdb.Subscribe(ctx, key...)
 defer time.AfterFunc(time.Second, func() {
  _ = sub.Close()
 })
 _, err := sub.Receive(ctx)
 if err != nil {
  return errors.New(err.Error())
 }
 // 设置channel参数 https://github.com/go-redis/redis/issues/1850
 ch := sub.Channel(
  redis.WithChannelSize(100),
  redis.WithChannelHealthCheckInterval(10*time.Second),
  redis.WithChannelSendTimeout(3000 * time.Second),
 )
 for msg := range ch {
  // todo 执行操作
  fmt.Println(msg.Channel, msg.Payload)
 }
 return nil
}

发布者

代码语言:javascript
复制
func Publish(key string, value interface{}) error {
 if key == "" || value == nil {
  return errors.New(common.ArgsIsNull)
 }
 rdb := config.GetRedis()
 err := rdb.Publish(ctx, key, value).Err()
 if err != nil {
  return errors.New(err.Error())
 }
 log.Infof("Publish key: %v, value: %v", key, value)
 return nil
}

注:如果需要Publish自定义结构体,需要实现MarshalBinary方法。

代码语言:javascript
复制
type Msg struct { Title   string `form:"title" json:"title" binding:"required"`     // 标题 Content string `form:"content" json:"content" binding:"required"` // 内容}

当我尝试Publish 自定结构体,就会这样的错误提示,这告诉我们需要将我们的结构体转为二进制形式

代码语言:javascript
复制
redis: can't marshal *dto.Msg (implement encoding.BinaryMarshaler)

这里有两种解决方法:1、我们可以把Msg结构体实现MarshalBinary方法,Redis将自动执行所有操作

代码语言:javascript
复制
func (m *Msg) MarshalBinary() (data []byte, err error) { return json.Marshal(m)}

2、或者可以选择在Publish前自己手动转为二进制形式

代码语言:javascript
复制
func  Test3(test *testing.T)  { msg := &dto.Msg{Title: "123123",Content: "123123"} marshal, err1 := json.Marshal(msg) if err1 != nil {  return } err := Publish(common.Sms,marshal) if err != nil {  log.Debug(err) }}

Redis小技巧

1、为什么要使用mset或者mget

“减少带带宽和io,因为redis需要封装resp协议(需要封装tcp协议与ip协议,各需要20byte),如果使用set或get去设置或获取(k,v)会封装两次,而是用mset k1 v1 k2 v2,就会减少封装次数,减少带宽和io。 ”

2、现在系统有千万级的活跃用户,如何实现日活统计,为了增强用户粘性,要上线一个连续打卡发放积分的功能,怎么实现连续打卡用户统计。

“使用setbit设值、getbit、bitcount统计 参考:https://blog.csdn.net/hgd613/article/details/54095729 ”

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-12-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 博思奥园 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • redis概念
  • 应用场景
  • String(最大存储512M)
  • Go中使用Redis
    • 安装
      • 配置连接
        • 编写存取逻辑
          • 使用
          • Redis 发布/订阅模式
            • 订阅者
              • 发布者
              • Redis小技巧
              相关产品与服务
              云数据库 Redis
              腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档