4.15redis点评项目下

发布于:2025-04-17 ⋅ 阅读:(101) ⋅ 点赞:(0)

--->接redis点评项目上

Redis优化秒杀方案

下单流程为:用户请求nginx--->访问tomcat--->查询优惠券--->判断秒杀库存是否足够--->查询订单--->校验是否是一人一单--->扣减库存--->创建订单

以上流程如果要串行执行耗时会很多,所以想要进行异步操作。

想法一:开启多线程,每一个线程处理一个步骤再做统一返回。

缺点:访问人很多的情况下,线程池的线程就会不够。

想法二:可以利用消息队列,先返回能确定完成事务1的信号,然后再直接进行判断事务能否完成.事务1和事务2可以等到所有判断结束后再慢慢进行。

总体思路:将耗时较短的(比如判断库存是否大于0(是的话判断用户是否可以下单)--->在set集合中查找用户是否已有下单记录保证一人一单(否的话将userId和优惠卷存入到redis中,并且返回0))判断逻辑放入redis中,然后及时返回结果给前端,之后判断当前redis返回结果是否为0(是的话表示可以下单)--->把一些优惠劵,用户订单等信息存入异步queue(阻塞队列)中,交给tomcat处理其他的判断逻辑(利用独立线程异步下单)。

基于阻塞队列的异步秒杀存在问题有:内存限制;数据安全。

Redis消息队列

消息队列:存储和管理消息,也被称为消息代理(Message Broker)

最简单的消息队列模型:

生产者--->消息队列--->消费者

使用消息队列的好处是:解耦。

耦合:一个事务必须要等另一个事务空闲才能运行或者传递数据。

秒杀中的消息队列模型对应的是:redis校验下单条件--->消息队列(传递id信息)--->启动一个新线程完成下单。

有一些现成的消息队列如kafka,rabbitmq等,也有一些redis提供的mq方案。

Redis消息队列-基于List实现消息队列

Redis基于List的消息队列的常见指令:

LPUSH 添加一个或多个元素插入到list的头部
LPOP 从 list 中删除并返回第一个元素
RPUSH 添加字符串元素到对应 list 的尾部
RPOP 从list 的尾部删除元素,并返回删除元素
LLEN 获取list的元素个数
LRANGE 获取列表指定范围内的元素
LINDEX 获取列表中指定位置的元素

但是当队列中没有消息时删除操作就会返回null,我们想要实现阻塞并等待消息的效果,就采用BRPOP(阻塞RPOP)或者BLPOP实现阻塞效果。

优点:基于Redis存储,不受限于JVM的内存上限;基于Redis的持久化机制,数据安全性有保证;可以满足消息有序性。

缺点:无法避免消息丢失;只支持单消费者。

小知识点:

Redis的持久化机制:为了保证突然宕机,内存数据不会全部丢失。

Redis提供了三种持久化机制:RDB、AOF和混合持久化。

RDB(Redis Database Snapshot)是通过创建内存数据的快照,将其保存到磁盘中。

AOF(Append Only File)通过记录每个写操作命令来实现持久化。每当执行一个会改变数据的命令时,Redis会将该命令写入AOF文件中。

混合持久化结合了RDB和AOF的优点。它首先以RDB格式保存当前数据状态,然后继续以AOF格式记录新的写操作。

Redis消息队列-基于PubSub的消息队列

消费者可以订阅一个或多个channel,生产者向对应channel发送消息后,所有订阅者都能收到相关消息。

SUBSCRIBE chan:订阅一个或多个频道。

PUBLISH chan msg:向一个频道发送信息。

PSUBSCRIBE pattern:订阅与pattern格式匹配的所有频道。

优点:采用发布订阅模式,支持多生产多消费。

缺点:不支持数据持久化;无法避免消息丢失;消息堆积有上限,超出时数据丢失。

Redis消息队列-基于Stream的消息队列

发送信息的命令:XADD

读取信息的方式之一:XREAD

XREAD特点:消息可回溯;一个消息可以被多个消费者读取;可以阻塞读取;有消息漏读的风险。

如何使用stream消息队列实现异步秒杀:

1.创建一个Stream类型的消息队列。

2.修改之前的秒杀Lua脚本,当用户拥有抢购资格之后(增加将用户id保存到队列id中,然后将优惠券,用户和队列id全部加到队列中)

3.开启一个新线程,获取消息队列中的订单信息,如果为空继续获取直到获取了信息开始解析数据,创建订单并确认信息;如果出现订单信息异常就重新获取、判断、解析、创建、确认信息。

消费者组?

达人探店功能

发布探店笔记

表主要有两部分:

探店笔记表:包含笔记中的标题、文字、图片等。

其他用户对该笔记的评价表。

查看探店笔记

点赞功能

基本要求:

一个用户只能点赞一次;

当用户已点赞,点赞按钮高亮显示;

当用户已点赞,再点一遍点赞按钮就实现取消点赞。

实现思路:采用一个set集合,当用户点赞时,就把用户id存入集合中,然后点赞数加一,如果用户取消点赞,就把用户id从该集合中移除,然后点赞数减一;下次再判断时,只需要判断set集合中有没有用户的id,就能判断该用户是否点赞过。

使用set集合的原因时:需要不重复的数据

点赞排行榜

之前用set是无法排序的,所以换成sortedset就可以实现。

然后只需要在已经排好序的sortset里找到id前五位就能实现点赞排行榜。

好友关注

关注和取消关注

创建一个关注表,粉丝为user_id;关注的博主为follow_user_id;

关注就新增数据,取关就从表中删除数据。

共同关注

实现思路:set集合中有交集并集补集的API,所以只需要把两个人关注的人分别放入两个set集合中,然后通过API查看两个set集合的交集数据。(intersect)

关注推送(feed流)

Feed流效果:系统分析用户想看什么,然后将内容推送给用户,使其节约时间,不用主动寻找。

Feed流的两种常见模式:

1,Timeline:不做内容筛选,简单的按照内容发布时间排序,常用于好友或关注。例如朋友圈。

2,智能排序:利用智能算法屏蔽掉违规的、用户不感兴趣的内容。推送用户感兴趣信息来吸引用户

 本项目采用Timeline模式,该模式有三种实现方案:

拉模式:(读扩散)在用户选择读信息时,将关注的人更新的内容全部拉取过来再排序,用户可以一次性看完之后清楚;但是缺点就是,如果用户关注的人太多信息太多,一次性拉取信息对服务器压力巨大。(多个关注的人在发件箱发的在一次读中全拉取过来到用户收件箱)

推模式:(写扩散)关注的人发一条消息这条消息就传递到粉丝收件箱中;时效快不用临时拉取但是如果粉丝太多就会有很多分数据到粉丝,内存压力大。

推拉结合模式:读写混合,兼具推和拉两种模式的优点。如果是粉丝量小的博主就采用推模式,如果是粉丝量大的博主,对于普通粉丝就采用拉模式,对于活跃度高的粉丝就采用推模式。

实现分页查询收邮箱:Feed流数据会不断更新,数据角标也会变化,比如一开始有十个数据,然后每次取固定数量5个数据(10-6),然后有新数据更新,变成了11个数据,然后再取第二页五个数据就变成6-2,而不是5-1.所以传统分页在Feed流中是不适用的。

为了实现分页操作,我们需要两个参数:用来记录上一次查询的最小时间戳lastid(作为下次分页的开始)和偏移量offset(每一页取几个数据)。

先从redis收件箱中按时间戳倒叙查询,跳过前offset个数据实现分页。获得数据之后判断是否为空,不为空更新时间戳,重置偏移量为确保下一次查询。

附近商户

可以采用GEO,先将商家按typeid分类,然后将type作为key,将所有的商家放入到一个GEO集合中。GEO在redis中就一个menber和一个经纬度,我们把x和y轴传入到redis做的经纬度位置去。然后调用GEO的相关函数就可以实现将商家按照距离排序,然后按照之前的方式进行分页,获得商户id,然后根据id拿到商家信息,进行结果的返回等操作。

用户签到

利用BitMap来实现签到,主体思路为:

BitMap(位图):把每一个bit位对应当月的每一天,形成了映射关系。用0和1标示业务状态(1表示已签到,0表示未签到),这种思路就称为位图(BitMap)。

Redis中是利用string类型数据结构实现BitMap,因此最大上限是512M,转换为bit则是 2^32个bit位。

可以将年月作为BitMap的key值,然后对应位置setBit实现签到。

连续签到天数:获得当前这个月的最后一次签到数据,定义一个计数器,然后不停的向前统计,直到获得第一个非0的数字即可,每得到一个非0的数字计数器+1,直到遍历完所有的数据,就可以获得当前月的签到总天数了。

怎么从后往前遍历每个bit位:因为BitMap返回的数据是十进制,让得到的十进制数字和1做与运算,因为1遇见1才是1(计数器++),其他数字都是0,结果数字每与一次就是将结果向右移动一次。

BitMap拓展:解决缓存穿透问题。

如果用户访问了一个数据库不存在并且redis中也不存在的数据,那就相当于进行了缓存穿透,对服务器来说相当于一次攻击。

我们解决方案是:将数据库中的数据所对应的id写入一个list中,用户访问时先在list中判断有没有Id数据,没有直接返回。

但是数据库主键id很长,我们可以采用哈希思想(查找某一个数据是否在里面)

具体思路是:我们创建一个利用string类型数据结构实现的BitMap;在BitMap找到索引为Id%bitmap.size位置,将其设置为1;用户将自己查询信息的id用相同的哈希算法计算,判断那个索引位置的数字是1还是0就可以判断出来数据到底在不在数据库里。

缺点:要考虑哈希冲突产生的误差。

以上内容来自于黑马程序员

后续会继续补一些内容


网站公告

今日签到

点亮在社区的每一天
去签到