0
点赞
收藏
分享

微信扫一扫

7. Go操作Redis数据库

数据类型应用场景

类型

简介

特性

场景

String(字符串)

二进制安全

可以包含任何数据,比如jpg图片或者序列化的对象,一个键最大能存储512M

---

Hash(字典)

键值对集合,即编程语言中的Map类型

适合存储对象,并且可以像数据库中update一个属性一样只修改某一项属性值(Memcached中需要取出整个字符串反序列化成对象修改完再序列化存回去)

存储、读取、修改用户属性

List(列表)

链表(双向链表)

增删快,提供了操作某一段元素的API

1,最新消息排行等功能(比如朋友圈的时间线) 2,消息队列

Set(集合)

哈希表实现,元素不重复

1、添加、删除,查找的复杂度都是O(1) 2、为集合提供了求交集、并集、差集等操作

1、共同好友 2、利用唯一性,统计访问网站的所有独立ip 3、好友推荐时,根据tag求交集,大于某个阈值就可以推荐

Sorted Set(有序集合)

将Set中的元素增加一个权重参数score,元素按score有序排列

数据插入集合时,已经进行天然排序

1、排行榜 2、带权重的消息队列

订阅和发布

什么是发布和订阅

发布订阅是一种应用程序(系统)之间通讯,传递数据的技术手段。特别是在异构(不同语言)系统之间作用非常明显。发布订阅可以是实现应用(系统)之间的解耦合。


发布订阅:类似微信中关注公众号/订阅号,公众号/订阅号发布的文章,信息。订阅者能及时获取到最新的内容。微博的订阅也是类似的。日常生活中听广播,看电视。都需要有信息的发布者,收听的人需要订阅(广播、电视需要调动某个频道)。发布订阅是一对多的关系。


订阅:对某个内容感兴趣,需要实时获取新的内容。只要关注的内容有变化就能立即得到通知。多的一方。


发布:提供某个内容,把内容信息发送给多个对此内容感兴趣的订阅者。是有主动权,是一的一方。

 

发布订阅应用在即时通信应用中较多,比如网络聊天室,实时广播、实时提醒等。滴滴打车软件的抢单;外卖的抢单;在微信群发红包,抢红包都可以使用发布订阅实现。

Redis的发布和订阅

Redis发布订阅(pub/sub)是一种消息通信模式:发送者(publish)发送消息,订阅者(subscribe)接收消息。发布订阅也叫生产者消费者模式,是实现消息队列的一种方式。Redis 客户端可以订阅任意数量的频道。

 

下图展示了频道 channel1 , 以及订阅这个频道的三个客户端 —— client2 、 client5 和 client1 之间的关系:

7. Go操作Redis数据库_发布订阅

 

当有新消息通过 PUBLISH 命令发送给频道 channel1 时, 这个消息就会被发送给订阅它的三个客户端

7. Go操作Redis数据库_消息队列_02

 

 

命令行模式举例

关于redis的订阅和发布,重点关注三个要点

  • psubscribe:订阅一个或多个符合给定模式的频道。返回值:接收到的信息
  • publish:publish channel message:将信息 message 发送到指定的频道 channel

返回值:接收到信息 message 的订阅者数量。

  • unsubscribe:指示客户端退订给定的频道

主要的操作命令

7. Go操作Redis数据库_发布订阅_03

redis发布与订阅的缺陷

虽然redis实现了发布订阅(publish/subscribe)的功能,但是在通常的情况下是不推荐使用的,如果想使用消息队列这种功能,最好还是使用专业的各种MQ中间件,例如rabbitMQ(阿里云开源消息队列),rockedMQ,activitedMQ,kafka(apache 开源的多消息队列,大数据生态中应用广泛)等。

 

代码方式举例

package main

 

import (

"fmt"

"github.com/my/repo/redisConn"

"log"

"sync/atomic"

"time"

)

 

var conn = redisConn.ConnectRedis()

var ctx = redisConn.CTX

 

func main() {

go subscribe()// 订阅

publish(6)// 信息发布

time.Sleep(1 * time.Second)

defer Reset()

}

 

func subscribe() {

sub := conn.Subscribe(ctx, "channel")

defer sub.Close()

 

var count int32 = 0

for item := range sub.Channel() { // 通过遍历Channel的执行结果来监听订阅消息

fmt.Println(item.String()) // 打印接收到的每条消息

atomic.AddInt32(&count, 1) // 接收一条订阅反馈消息

fmt.Println(count)         // 3条发布者发送的消息

switch count {

case 4: // 执行退出操作,停止监听新消息

if err := sub.Unsubscribe(ctx, "channel"); err != nil {

log.Println("unsubscribe faile, err:", err)

} else {

fmt.Println("unsubscribe success")

}

}

}

}

 

func publish(n int) {

time.Sleep(1 * time.Second) // 休眠,让订阅者有足够的时间连接服务器并监听消息

for n > 0 {

var url = "hello=%d"

msg := fmt.Sprintf(url,n )

conn.Publish(ctx, "channel", msg)

n--

}

}

 

//Redis Flushdb 命令用于清空当前数据库中的所有 key。

func Reset() {

conn.FlushDB(ctx)

}

 

pipeline

 

​​https://www.cnblogs.com/-wenli/p/12922089.html​​

 

为什么使用 Pipeline

Redis客户端执行一条命令分为如下四个过程:

 

发送命令

命令排队

命令执行

返回结果

 

Redis的pipeline功能的原理是 客户端通过一次性将多条redis命令发往Redis 服务端,减少了每条命令分别传输的IO开销。同时减少了系统调用的次数,因此提升了整体的吞吐能力。

 

未使用pipeline命令

7. Go操作Redis数据库_发布订阅_04

 

 

 

使用了pipeline命令

7. Go操作Redis数据库_redis_05

 

 

 

 

 

用户基础画像(hash应用)

业务需求

在推荐系统中,用户基础画像时非常重要的,例如:我们可以根据用户的性别/年龄的特征,来选择那些该用户喜欢的商品,为提高系统性能,一般采用通过缓存系统来存储(例如:Redis)

 

7. Go操作Redis数据库_发布订阅_06

 

数据结构设计

key(用户ID)

特征名称

特征数值

user:234

用户名称

vx-aiwen

联系方式

1121025735

博客

wenjie.blog.csdn.net

性别

male

年龄

30

任务实现

通过代码实战方式,完成用户基础画像方案

1、用户基础画像存储

2、针对用户ID,获取用户画像信息

 

 

 

key := "user:234"

conn.HSet(ctx, key, "username", "vx-aiwen")

conn.HSet(ctx, key, "password", "1121025735")

conn.HSet(ctx, key, "blog", "wenjie.blog.csdn.net")

conn.HSet(ctx, key, "sex", "male")

conn.HSet(ctx, key, "age", 30)

 

微博热榜列表(zset 应用)

业务需求

7. Go操作Redis数据库_消息队列_07

 

热榜任务设计

zset 的 key,把文章ID 作为 member ,点击数评论数等作为 score,当 score 发生变化时更新 score。利用 ​​ZREVRANGE​​ 或者 ​​ZRANGE​​ 查到对应数量的记录。

​​https://blog.csdn.net/weichi7549/article/details/107836536​​

​​https://blog.csdn.net/weichi7549/article/details/107836536​​

 

  • 离线任务存储

贴子ID 作为 member ,点击数评论数等作为 score

  • 在线热榜推荐

 

热榜任务实现

通过实战代码,最终实现rank列表,然后在界面上进行展示

rank

得分

文章ID

文章标题

0

265.2

articleID_1000

..

1

166.7

articleID_3000

....

2

166.5

articleID_2000

.....

3

166.2

articleID_4000

.......

 

package main

 

import (

"fmt"

 

"github.com/aiwen/aiwein-go-redis/redisConn"

"github.com/go-redis/redis/v8"

)

 

// key 设计

var (

key = "weibo_hot"

)

 

type WeiboHotAPI struct {

rdb *redis.Client

}

 

var api = WeiboHotAPI{rdb: redisConn.ConnectRedis()}

 

func main() {

//离线任务存储

api.store()

//人榜列表获取

api.hotList()

}

 

func (api *WeiboHotAPI) store() error {

client := api.rdb

client.ZAdd(api.rdb.Context(), key, &redis.Z{

Score:  265.2,            // 点击或者阅读综合得分

Member: "articleID_1000", // 文章ID

})

client.ZAdd(api.rdb.Context(), key, &redis.Z{

Score:  166.5,

Member: "articleID_2000",

})

client.ZAdd(api.rdb.Context(), key, &redis.Z{

Score:  166.7,

Member: "articleID_3000",

})

client.ZAdd(api.rdb.Context(), key, &redis.Z{

Score:  166.2,

Member: "articleID_4000",

})

client.ZAdd(api.rdb.Context(), key, &redis.Z{

Score:  162.5,

Member: "articleID_5000",

})

return nil

}

 

func (api *WeiboHotAPI) hotList() {

client := api.rdb

// 获取排行榜

// 前三列表

rank_list := client.ZRevRangeWithScores(api.rdb.Context(), key, 0, 3).Val()

fmt.Println("rank_list = ", rank_list)

for rank, data := range rank_list {

fmt.Println("rank = ", rank, ",articleID = ", data.Member, "score = ", data.Score)

}

}

 

 

 

 

 

 

 

 

举报

相关推荐

0 条评论