redis应用场景真多 - 3 redis 应用场景和数据类型
wptr33 2024-12-25 16:01 17 浏览
有关Redis的应用场景介绍终于来到第三篇了,下面继续贡献其它场景的使用介绍。
一。商品和用户维度计数
1.1,说起电商,肯定离不开商品,而附带商品有各种计数(喜欢数,评论数,鉴定数,浏览数,etc)
Redis的命令都是原子性的,你可以轻松地利用INCR,DECR等命令来计数。不过,使用hash 结构也是不错的选择。
为product定义个key product:,为每种数值定义hashkey, 譬如喜欢数like_num。
$redis->hSet('product:123', 'like_num ', 5); // 添加 id为123的商品 like_num 为5
$redis->hIncrBy('product:123', 'like_num ', 1); // 添加 id为123的商品like_num +1
$redis->hGetAll('product:123'); // 获取id为123的商品相关信息
1.2,对用户动态数、关注数、粉丝数、喜欢商品数、发帖数等计数
用户维度计数同商品维度计数都采用 Hash. 为User定义个key 为 user:
为每种数值定义hashkey, 譬如关注数follow
$redis->hSet('user:100000', 'follow ', 5); // 添加uid为10000的用户follow 为5
$redis->hIncrBy('user:100000', 'follow ', 1); // 更新uid为10000的用户follow +1
$redis->hGetAll('user:100000'); // 获取uid为10000的用户
二。存储社交关系
譬如将用戶的好友/粉丝/关注,上一篇的最后也初步介绍了利用集合进行社交关系的存储,这次介绍利用有序集合sorted set来存,score可以是 timestamp 。
默认集合按照score递增排序
这样求两个人的共同好友的操作,可能就只需要用求交集命令即可。
$redis->zAdd('user:1000:follow', 1463557212, '1001');
#uid为1000用户关注uid为1001 , score值设定时间戳1463557212
$redis->zAdd('user:1000:follow', 1463557333, '1002');
$redis->zAdd('user:2000:follow', 1463577568, '1001');
$redis->zAdd('user:2000:follow', 1463896964, '1003');
#uid为2000用户关注1001和1003用户 , score值设定时间戳
$redis->zInter('com_fllow:1000:2000', array('user:1000:follow', 'user:2000:follow'));
#对集合'user:1000:follow'和'user:2000:follow'取交集'com_fllow:1000:2000'
#获得共同关注的uid
$redis->zRange('com_fllow:1000:2000',0,-1); // 获取全部集合元素
三。反spam系统
应用系统评论、发布商品、论坛发贴的spam控制,作为一个电商网站被各种spam攻击是少不免(垃圾评论、发布垃圾商品、广告、刷自家商品排名等),针对这些spam制定一系列anti-spam规则,其中有些规则可以利用redis做实时分析。
譬如:1分钟评论不得超过2次、5分钟评论少于5次等(更多机制/规则需要结合drools );
常规sorted set将最近一天用户操作记录起来。
#获取5秒内操作记录
$res = $redis->zRangeByScore('user:1000:comment', time() - 5, time());
#判断5秒内不能评论
if (!$res) {
$redis->zAdd('user:1000:comment', time(), '评论内容');
} else {
echo '5秒之内不能评论';
}
#5秒内评论不得超过2次
if($redis->zRangeByScore('user:1000:comment',time()-5 ,time())==1)
echo '5秒之内不能评论2次';
#5秒内评论不得少于2次
if(count($redis->zRangeByScore('user:1000:comment',time()-5 ,time()))<2)
echo '5秒之内不能评论2次';
四。最新列表&排行榜列表版
用于记录用户刚刚喜欢的商品最新列表or排行榜 等业务场景.
商品最新列表-sorted set结构呈现
$redis->zAdd('user:1000:product:like', time(), '3002');
$redis->zAdd('user:1000:product:like', time(), '3001');
$redis->zAdd('user:1000:product:like', time(), '3004');
$redis->zAdd('user:1000:product:like', time(), '3003');
$redis->zRange('user:1000:product:like', 0, -1,true);
#默认喜欢时间升序序排列
#
Array(
[3002] => 1463565179
[3001] => 1463565189
[3004] => 1463565199
[3003] => 1463565209
)
$redis->zRevRange('user:1000:product:like', 0, -1,true);
#以喜欢时间降序排列
#
Array
(
[3003] => 1463565424
[3004] => 1463565414
[3001] => 1463565404
[3002] => 1463565394
)
排行榜-list数据结构呈现
$redis->lPush('user:1000:product:like', '3002');
$redis->lPush('user:1000:product:like', '3001');
$redis->lPush('user:1000:product:like', '3004');
$redis->lPush('user:1000:product:like', '3003');
$redis->lRange('user:1000:product:like', 0, -1);
#Array
(
[0] => 3003
[1] => 3004
[2] => 3001
[3] => 3002
)
五。消息通知
采用Hash结构对消息通知业务场景计数
$redis->hSet('user:1000:message:notice', 'system', 1);
#设置1条未读系统消息
$redis->hIncrBy('user:1000:message:notice', 'system', 1);
#未读系统消息+1
$redis->hSet('user:1000:message:notice', 'comment', 1);
#设置1条未读评论
$redis->hIncrBy('user:1000:message:notice', 'comment', 1);
#未读评论+1
$redis->hGetAll('user:1000:message:notice');
#查看所有消息通知数量
Array
(
[system] => 2
[comment] => 2
)
六。微博底下显示最新的评论
像微博这些一个热门帖子就有其他的消息量,所以使用redis来进行数据的浏览和操作是很好的选择
存储模型设计
redis中存储数据的模型主要是list和hash,list用于有序地将评论id存储,hash则是根据id来存储评论内容。
数据id的设计
所有的评论都存储于MySQL的一张表中
对于存储评论id的list来说,因为需要指定是哪一条微博,所以key的名称应该要有该条微博的id -> weibo:66:comments,而这个key对应的值是每条评论的id -> $commentId ;
而对于存储评论内容的hash结构,因为是存储着每条评论的具体内容,所以每个键值对就是 id -> 内容的形式,key需要指定是这条微博 -> weibo:66 :comments:content
value的值则是按照评论id和评论内容来存储 $commentId – $commentContent
当用户评论之后
存入Mysql数据库,获取到的评论id
评论id通过push进redis的list中,并且将评论内容转成json的格式存放进redis的hash中
删除该条微博5000条之后的评论(完整的评论数据存在于MySQL数据库中)
伪代码:
public void addComment(Comment comment){
Long commentId = commentMappper.insert(comment);
String listKey = "weibo:"+comment.getWeiboId+"comments";
String hashKey = "weibo:"+comment.getWeiboId+":comment:content";
jedis.lpush(listKey,commentId.toString());
jedis.hset(hashKey,commentId.toString,comment.getConent());
//只要最新的5000条数据存在redis中
List ids = jedis.lrange(listKey,5000,-1);
for(String id : ids){
jedis.hdel(hashKey,id);
}
jedis.ltrim(listKey,0,5000);
}
当有用户查看时
按照分页数据从list中取出对应的评论id
如果没有查到数据(用户需要5000条之后的数据),或者需要查询的数据较小,则从mysql中查出,否则从redis中查询
伪代码:
public List queryComments(Long weiboId,QueryObject qo){
String listKey = "weibo:"+weiboId+"comments";
String hashKey = "weibo:"+weiboId+":comment:content";
Long start = (qo.getCurrentPage - 1) * qo.getPageSize;
Long end = start + qo.getPageSize - 1; // qo.getCurrentSize()*qo.getPageSize()-1;
List ids = jedis.lrange(listkey,start,end);
List result = new ArrayList<>();
if(ids.size commentMapper.queryForPage(qo);//SELECT * from comment ORDER BY time DESCLIMIT #{startIndex},{pageSize}
} else{
for(String id : ids){
String conent = jedis.hget(hashKey,id);
result.add(conent);
}
}
return result;
}
当有用户删除评论时
将评论id获取,删除redis的数据,list和hash都要
删除mysql中的数据
伪代码:
public void delete(Long weiboId,Long commentId){
String listKey = "weibo:"+weiboId+"comments";
tring hashKey = "weibo:"+weiboId+":comment:content";
jedis.lrem(listKey,commentId,1);
jedis.hdel(hashKey,commentId);
commentMapper.delete(Long commentId);
}
七。最近浏览功能
当一个需求是获取最近的浏览功能的时候,其实这个数据模型就应该马上出来,起码是有序对的存储,所以使用redis的list和zset都可以实现,使用list的时候只需要简单地保存就可以了,但是需要注意如果相近的两天浏览了相同商品,那么这个商品的浏览记录应该是最近的,也就是我们要删除之前的。同时,我们还需要设置(因为redis的过期删除是删除整个key,所以这里的有效时间是整个浏览历史的有效时间),而且浏览记录需要设置一个数量
存储模型设计
主要存储商品的id和所对应的用户的信息即可,商品的具体内容可以从mysql数据库中查出来。
数据id的设计
每一个key对应一个用户的浏览记录key -> MemeberRecentGoods:66 ,所存储的值就是商品的id即可
存储用户浏览的商品
避免重复,先删除之前关于这条商品id的浏览记录(如果是用zset来做,就不需要,因为zset会自己去重,所以设置最新的时间大小即可)
存入对应的list
只保留最近的60个
重新给这个key设置时间
伪代码:
public void addMemeberRecentGoods(Long memberId,Long templateId){
String key = "MemeberRecentGoods:"+memberId;
jedis.lrem(key,1,templateId.toString());
jedis.lpush(key,templated);
jedis.ltrim(key,0,59);
jedis.expire(key,60*60*24*30);
}
读取用户最近浏览的商品
根据用户的id,分页信息取出商品id
将总页数,商品信息等数据封装进map保存
伪代码:
public Map queryMemberRecentGoods(Long memberId, QueryObject qo){
String key = "MemeberRecentGoods:"+memberId;
Long start = (qo.getCurrentPage - 1) * qo.getPageSize;
Long end = start + qo.getPageSize - 1; // qo.getCurrentSize()*qo.getPageSize()-1;
//获取总共条数,算出有多少页用于前台显示
Long totalCount = jedis.llen(key);
Integer totalPage = totalCount/pageSize == 0 ? totalCount/pageSize : totalCount/pageSize + 1;
List ids = jedis.lrange(key,start,end);
//查询出商品的信息(可以保存在数据库,也可以保存在redis)
List goods = recentGoodsService.get(ids);
Map map = new HashMap<>();
map.put("goods",goods);
map.put("totalCount",totalCount);
return map;
}
八。实现日排行、周排行
很多应用都有排行榜的功能,排行榜有两个特点,其一是按积分排名,其二就是需要有时间约束,排行榜是有时效性的,如果排行榜没有时效,那始终都是老用户在榜首,这对新用户并不公平,使用redis可以很高效地实现这个功能。
存储模型设计
因为需要用到积分排行,其实就是按照内容进行排行,使用ZSet可以做到,在这个应用中,每天都有一个排行榜。
数据id设计
zset所对应的key需要指定日期,而用户id作为成员 key -> rank:20190115 member ->
key所对应的值就是数字
增加积分
伪代码:
public void addScore(String nowDate, Long userId, Integer score){
String key = "rank:"+nowDate;
jedis.zincrby(key,score,userId);
}
获取每日排行
选出前10名展示出来
伪代码:
public List getDailyRankings(String nowDate){
String key = "rank:"+nowDate;
return jedis.zrevreange(key,0,9);
}
获取每周排行
利用zset的并集实现一周的日排行相加,从而选出周排行
伪代码:
public List getWeeklyRankings(String[] keys){
String weeklyRankings = "weeklyRankings";
jedis.zunionstore(weeklyRankings,2,keys[0],keys[1],keys[2],keys[3],keys[4],keys[5],keys[6],"weights",1,1,1,1,1,1,1);
return jedis.zrevrange(weeklyRankings,0,9);
}
九。实现文章按点击量排名
文章点击量的排行,其实主要是利用了redis的单线程自加来实现的。
存储模型设计
一个用来存储文章,一个用来存储该文章的点赞数,利用hash来存储文章内容,使用一个zset存储文章的点击量。
数据id设计
需要指定到某一个人的文章,所以对于文章的存储,使用hash来存储某个用户的哪篇文章,需要用户id以及文章id,
而构造一个zset的存储空间来存储每篇文章的点赞数,并且利用zset来进行点赞后的加一操作。
存储文章
HSET user:00001 “article:000001” “文章”
HSET user:00002 “article:000001” “文章”
文章排序
ZADD article:score 0 user:00001:artical:0000001
ZADD article:score 0 user:00002:artical:0000001
用户点赞
ZINCRBY article:score 1 user:00001:artical:0000001
ZINCRBY article:score 1 user:00002:artical:0000001
根据点击量获取文章列表
ZREVRANGEWITHSCORES article:score 0 -1
十。上亿用户登录统计数据
存储模型设计
因为有上亿的用户量,所以可以将每个用户按照每个位来存入,每一天都有一个bitmap,每个bitmap的每一位对应着一个用户的当天的登录状态值
数据id设计
每天的bitmap的key设置成当天日期,比如login_20181206,而用户的id就和位数对应,比如王五的id为8,则bitset login_20181206 8 1
需求:获取最近三天都登录的用户
bitop and login_last_3_day login_20181206 login_20181205 login_20181204
(其实就是取三天的二进制位进行与运算)
需求:获取最近7天曾经登录的用户:(最近7天有一天登录即可)
bitop or login_last_7_day login_20181206 login_20181205 login_20181204 login_20181203 login_20181204 login_20181205 login_20181206
需求:获取最近7天都没有登录的用户
bitop not nerver_login_last_7_day login_last_7_day
需求:统计某一用户当前月的登录天数
方法一:
使用bitmap存储所有用户的当天的登录情况,在java代码中循环30次取得每一天某一个用户的状态,并统计
方法二:
使用bitmap存储某一个用户的这个月的登录情况,1号是第0位,2号是第1位,以此类推,只需要8个字节就能解决某一个用户一个月的登录情况,取出来的时候只需要使用bitcount来查询即可。
十一。知乎回答的踩赞功能
存储模型设计
利用String或者hash来存储回答的内容,利用zset来存储每一个回答的分数,每条回答都有相对应的2个bitmap,一个用来当做用户的赞表,一个用来当做用户的踩表。
数据id设计
回答的id需要设计到回答者的id以及对应评论的id还有自己的id;
在zset中,以article:00001:answer:score作为key,需要绑定对应的文章id,回答的id作为member来存储每个回答的分数;
bitMap中需要知道当前回答的id,里面存储的是每个用户对这条回答的行为,1表示点了赞或者是点了踩,0表示没有操作
实现
当李四看到某个回答,要显示他是否支持或者反对(假设李四id为6)
第一次进来肯定还没有支持或反对. =====>结果为0,说明没有点赞.接着判断是否反对
getbit answer:166:user:oppose 6 =====>结果为0,说明没有反对.
此时李四对这个答案点赞.
先根据权重算出分数:1*2 = 2
zincrby answer:score 2 166
此时id=166号的答案就增加了两分.同时要标记李四已经对这个答案点赞了.
第二次进来,已经点赞了
getbit answer:166:user:goods 6 =====>结果为1,说明对这个答案是点赞了,返回前台,显示支持效果。(已经知道点赞就没有去查询是否点反对了)
不小心点了支持,完成了上面的动作,这时候我需要点反对。
我们需要做的动作是:
判断是否点赞了(防止前台绕过)
getbit answer:166:user:goods 6 ===>结果为1,说明点赞了,继续后面操作.
在 answer:166:user:goods把该用户对于的位修改成0,表示没有点赞.
setbit answer:166:user:goods 6 0
id=166的答案要先减去之前点赞所加的分数.
Id=166的答案要减去该用户点赞需要扣的分数.(分数可以一起统计后再扣除)
score = 12 + 0.52 = 3
zincrby answer:score -3 166
第三次进来,此时点击了反对
getbit answer:166:user:goods 6 =====>结果为0,说明没有点赞.接着判断是否反对
getbit answer:166:user:oppose 6 =====>结果为1,说明点了反对,返回前台,显示反对的效果.
按照分数对答案进行排序:
zrevrange answer:score 0 -1
点击id=166的答案显示该答案的点赞数:
bitcount answer:166:user:goods
其它场景待续。。。
相关推荐
- Python自动化脚本应用与示例(python办公自动化脚本)
-
Python是编写自动化脚本的绝佳选择,因其语法简洁、库丰富且跨平台兼容性强。以下是Python自动化脚本的常见应用场景及示例,帮助你快速上手:一、常见自动化场景文件与目录操作...
- Python文件操作常用库高级应用教程
-
本文是在前面《Python文件操作常用库使用教程》的基础上,进一步学习Python文件操作库的高级应用。一、高级文件系统监控1.1watchdog库-实时文件系统监控安装与基本使用:...
- Python办公自动化系列篇之六:文件系统与操作系统任务
-
作为高效办公自动化领域的主流编程语言,Python凭借其优雅的语法结构、完善的技术生态及成熟的第三方工具库集合,已成为企业数字化转型过程中提升运营效率的理想选择。该语言在结构化数据处理、自动化文档生成...
- 14《Python 办公自动化教程》os 模块操作文件与文件夹
-
在日常工作中,我们经常会和文件、文件夹打交道,比如将服务器上指定目录下文件进行归档,或将爬虫爬取的数据根据时间创建对应的文件夹/文件,如果这些还依靠手动来进行操作,无疑是费时费力的,这时候Pyt...
- python中os模块详解(python os.path模块)
-
os模块是Python标准库中的一个模块,它提供了与操作系统交互的方法。使用os模块可以方便地执行许多常见的系统任务,如文件和目录操作、进程管理、环境变量管理等。下面是os模块中一些常用的函数和方法:...
- 21-Python-文件操作(python文件的操作步骤)
-
在Python中,文件操作是非常重要的一部分,它允许我们读取、写入和修改文件。下面将详细讲解Python文件操作的各个方面,并给出相应的示例。1-打开文件...
- 轻松玩转Python文件操作:移动、删除
-
哈喽,大家好,我是木头左!Python文件操作基础在处理计算机文件时,经常需要执行如移动和删除等基本操作。Python提供了一些内置的库来帮助完成这些任务,其中最常用的就是os模块和shutil模块。...
- Python 初学者练习:删除文件和文件夹
-
在本教程中,你将学习如何在Python中删除文件和文件夹。使用os.remove()函数删除文件...
- 引人遐想,用 Python 获取你想要的“某个人”摄像头照片
-
仅用来学习,希望给你们有提供到学习上的作用。1.安装库需要安装python3.5以上版本,在官网下载即可。然后安装库opencv-python,安装方式为打开终端输入命令行。...
- Python如何使用临时文件和目录(python目录下文件)
-
在某些项目中,有时候会有大量的临时数据,比如各种日志,这时候我们要做数据分析,并把最后的结果储存起来,这些大量的临时数据如果常驻内存,将消耗大量内存资源,我们可以使用临时文件,存储这些临时数据。使用标...
- Linux 下海量文件删除方法效率对比,最慢的竟然是 rm
-
Linux下海量文件删除方法效率对比,本次参赛选手一共6位,分别是:rm、find、findwithdelete、rsync、Python、Perl.首先建立50万个文件$testfor...
- Python 开发工程师必会的 5 个系统命令操作库
-
当我们需要编写自动化脚本、部署工具、监控程序时,熟练操作系统命令几乎是必备技能。今天就来聊聊我在实际项目中高频使用的5个系统命令操作库,这些可都是能让你效率翻倍的"瑞士军刀"。一...
- Python常用文件操作库使用详解(python文件操作选项)
-
Python生态系统提供了丰富的文件操作库,可以处理各种复杂的文件操作需求。本教程将介绍Python中最常用的文件操作库及其实际应用。一、标准库核心模块1.1os模块-操作系统接口主要功能...
- 11. 文件与IO操作(文件io和网络io)
-
本章深入探讨Go语言文件处理与IO操作的核心技术,结合高性能实践与安全规范,提供企业级解决方案。11.1文件读写11.1.1基础操作...
- Python os模块的20个应用实例(python中 import os模块用法)
-
在Python中,...
- 一周热门
-
-
C# 13 和 .NET 9 全知道 :13 使用 ASP.NET Core 构建网站 (1)
-
因果推断Matching方式实现代码 因果推断模型
-
git pull命令使用实例 git pull--rebase
-
面试官:git pull是哪两个指令的组合?
-
git 执行pull错误如何撤销 git pull fail
-
git pull 和git fetch 命令分别有什么作用?二者有什么区别?
-
git fetch 和git pull 的异同 git中fetch和pull的区别
-
git pull 之后本地代码被覆盖 解决方案
-
还可以这样玩?Git基本原理及各种骚操作,涨知识了
-
git命令之pull git.pull
-
- 最近发表
- 标签列表
-
- git pull (33)
- git fetch (35)
- mysql insert (35)
- mysql distinct (37)
- concat_ws (36)
- java continue (36)
- jenkins官网 (37)
- mysql 子查询 (37)
- python元组 (33)
- mybatis 分页 (35)
- vba split (37)
- redis watch (34)
- python list sort (37)
- nvarchar2 (34)
- mysql not null (36)
- hmset (35)
- python telnet (35)
- python readlines() 方法 (36)
- munmap (35)
- docker network create (35)
- redis 集合 (37)
- python sftp (37)
- setpriority (34)
- c语言 switch (34)
- git commit (34)