redis快速入门5 五种数据类型之zSet类型 教你做一个简单的推荐系统

5 篇文章 0 订阅
5 篇文章 0 订阅

前期文章:
redis快速入门1 五种数据类型之String篇 你的个人网站访问统计怎么做
redis快速入门2 五种数据类型之Lists篇 好好排队,别挤了,会轮到你的!!
redis快速入门3 五种数据类型之hash篇 你别铺张浪费了!要精打细算呀!
redis快速入门4 五种数据类型之Set类型 你的博客点赞功能该如何实现

大家好,我是希望成为有暖度的理工男-啊ham.

前言

五一用来休息了,现在将redis五种常见数据类型中最后的一种,zset类型的讲解完。

可以说,zset的数据类型是redis这几个数据类型中,最复杂,也是最灵活的一种类型。它的中文翻译,为有序集合,顾名思义,他的数据排列是按照一定的顺序排列的,基于这个特性,我们也经常用zset类型来存储需要排行的数据。

ZSET

ZSET的结构

说到zset的结构,我们需要先回顾下set的数据结构是怎样的:
在这里插入图片描述
其中字符型的set结构,是基于字典来实现的,其中字典的值为null。有序集合zset的结构从表现形式上说,也是基于该结构,不同的点在于,存储数据的字典,它的值不为null,而是我们设置的score,可理解为key的权重值。如我们保存我们最喜欢的几种水果:
在这里插入图片描述
那么,我们查询favorite fruit的时候,他就会按数值大小顺序排列返回

常用指令

有序集合的指令比较多,我们这阶段可以基于目标来学习,也即是完成最常用的,或即将使用的指令进行学习。

ZADD 添加元素

#ZADD zset score member
127.0.0.1:6379> ZADD "favorite fruit" 1 Apple
(integer) 1
127.0.0.1:6379> ZADD "favorite fruit" 2 Banana
(integer) 1
127.0.0.1:6379> ZADD "favorite fruit" 3 Orange
(integer) 1

ZADD 指令执行成功后,会返回添加成功的元素数量, 我们也可以一次性地录入多个元素,如
ZADD “favorite fruit” 1 Apple 2 Banana 3 Orange;
另外, ZADD也可更新成员的分值, 如,我们觉得香蕉才是最爱,则可将Apple和Banana的分值对调

127.0.0.1:6379> ZADD "favorite fruit" 2 Apple
(integer) 1
127.0.0.1:6379> ZADD "favorite fruit" 1 Banana
(integer) 1

注意: 这里的更新是更新成员的分值,如果你想把苹果改为桃子peach,操作要分两步,移除apple, 添加peach。这是因为字典的key值是不具备变更的。

参数XX 只执行更新

ZADD 可支持可选参数来显示执行相应的指令,XX 表示ZADD将只执行对已存在的元素进行更新

# ZADD zset [XX] score member
127.0.0.1:6379> ZADD "favorite fruit" XX 4 Orange
(integer) 0
127.0.0.1:6379> ZADD "favorite fruit" XX CH 3 Orange
(integer) 1

我们尝试将Orange的分数从3改成4,发现返回值为0,这是因为redis返回值是表示当前新增的元素数量,如果我们想知道是否修改,可再加多一个参数CH

参数NX 只执行新增

NX 参数则表示只执行对未存在的元素进行新增,对已存在的元素不做任何操作,如,我们将peach 添加到我们喜欢的水果中

127.0.0.1:6379> ZADD "favorite fruit" NX 4 Peach
(integer) 1

ZREM 移除元素

比如,我们不喜欢桃子了,想把桃子从最喜欢的水果集合中移除

#ZREM zset member 
127.0.0.1:6379> ZREM "favorite fruit" Peach
(integer) 1

如果我们想批量移除,可以多个元素一起移除,指令成功后,会返回移除元素的数量

ZSCORE 获取元素的分值

我们想查看下在我的心目中,苹果是不是我的最爱

#ZSCORE zset member 
127.0.0.1:6379> ZSCORE "favorite fruit" Apple
"1"

可以看出,苹果排在第一位

ZINCRBY 对元素的分值自增

之前我们把香蕉放在了第二位,有天吃腻了,觉得橘子比香蕉好吃,我们就把香蕉的排位往后移一位,橘子往前移一位

#ZINCRBY zset number member 
127.0.0.1:6379> ZINCRBY "favoriate fruit" 1 Banana
"3"
127.0.0.1:6379>  ZINCRBY "favoriate fruit" -1 Orange
"2"

注意, incrby是将增加number, 如果要做减法,则通过加负数的形式完成减法操作

ZCARD 查询集合的大小

现在我们看看我们记录的最喜欢的水果有多少种

# ZCARD zset
127.0.0.1:6379> zcard "favorite fruit"
(integer) 4

ZRANGE 获取指定范围内的元素

我们想把前三名的水果都查出来,则可以通过zrange来查出来

# ZRANGE zset start end
127.0.0.1:6379>  ZRANGE "favorite fruit" 0 2
1) "Apple"
2) "Orange"
3) "Banana"

这里的start end 是从0开始的, 0,1,2即是前三位

ZRANK 获取元素在集合中的排名

127.0.0.1:6379> zrank "favorite fruit" Apple
(integer) 0

由此可见,如果我们把程序上的顺序转化成用户可理解的顺序的话,需要将集合的排名值加一

推荐系统核心实现

好了,我们来到推荐系统的核心实现环节,可以说,推荐系统在我们生活中经常可见,如我经常逛的b站:
在这里插入图片描述
我平时喜欢财经、IT,休闲时看看LOL,也关注了观察者网,和几位中美家庭的up主,然后他的推荐还是挺符合这些特征的;

再比如微信读书的猜你喜欢
在这里插入图片描述
基本上是基于我们阅读了哪些书籍,然后基于这些书的分类,推荐同类数据

基于这些,我们可以抽象出这里的简易版的推荐逻辑,我们把书籍和视频等,视为对象,则通过对用户浏览对象的特征进行记录,记录哪些特征是最多次数浏览的特征,然后将符合这些特征的其他对象推荐给用户。

好,我们依旧用nodejs 实现推荐的核心逻辑

/**
 * @author lamwimham
 * @date 2021-05-09
 */
 const redis = require('redis');


 // 我们先建模
 class User {
   constructor(data) {
     this.name = data.name;
     this.id = data.id
   }
 }
 class Target {
   constructor(data) {
     this.name = data.name; // 对象的名称
     this.category = data.category // 对象的特征分类
   }
 }

 class Recommender {
  constructor(key) {
    //TODO: 1. 连接redis服务器
    this.redisCli = redis.createClient();
    this.categoryKey = key; // 推荐的用户
    this.categorySet; // 对象特征分类集合
  }

/**
 * 记录浏览对象的特征次数
 * @param {Target} target 
 */
  recordTarget(target) {
    const codeStatus = this.redisCli.ZSCORE(this.key, target.category); // 查询key对象是否存在特征category
    if (codeStatus) {
      this.redisCli.ZINCRBY(this.key, 1, target.category) // 如果存在,则加1
    } else {
      this.redisCli.ZADD(this.key, 1, target.category) // 如果不存在,则初始化该特征值
    }
  }

  /**
   * 查询最值得推荐的前几名特征
   * @param {Number} num 
   */
  getRange(num) {
    return this.redisCli.ZRANGE(this.key, 0, num-1)
  }


 }


 // 应用实例: 图书推荐

 // 阅读用户: 张三
 const 张三 = new User({id: 1, name: "张三"})

 // 书籍
 const book1 = new Target({name: "redis实践", category: "redis"});
 const book2 = new Target({name: "乔布斯产品圣经", category: "经管"});
 const book3 = new Target({name: "你的灯亮着吗", category: "经管"});
 const book4 = new Target({name: "数据建模", category: "计算机"});
 const book5 = new Target({name: "深入浅出Mysql", category: "计算机"});

// 实例一个对张三的推荐对象
let recommender = new Recommender(张三.id + '图书推荐');
// 记录张三的阅读行为
recommender.recordTarget(book1);
recommender.recordTarget(book2);
recommender.recordTarget(book3);
recommender.recordTarget(book4);
recommender.recordTarget(book5);

// 给张三推荐图书时,前两类书籍是
const result = recommender.getRange(2);
console.log(result)// 经管、计算机
//  然后从书籍库中匹配出和经管和计算机的书,推送到张三的浏览页面上。。

当然,这里的推算并不能算是严格意义上的可用推荐逻辑。有兴趣的同学,可以了解下KN邻近算法,作为推荐算法的入门。

课后作业

大家可以尝试下,用python来实现推荐逻辑。

总结

好了,这次的讲解到此为止了。redis的五种常见数据类型已经讲完了。redis还有几个高级用法的数据类型,如HyperLogLog、bitmap,geo, 其中bitmap 我已经在第一讲String中已经讲了他的一个应用场景。剩下的再找时间拓展,以我的思路,下一步,会先讲redis的发布订阅。

我的坚持离不开你的关注,点赞!谢谢!我是希望成为有暖度的理工男-啊ham。我们下期再见

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lamwimham1

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值