黑马点评项目笔记

短信登录

基于session的短信登录

  1. 发送验证码,session保存验证码。
  2. 验证验证码,session保存用户信息。
  3. 校验登录状态,判断session里是否保存用户信息。

使用ThreadLocal保存用户信息,是方便后面业务获取当前用户,避免传参和频繁从session中取对象。

基于Redis实现共享session登录

集群的session共享问题:多台tomcat不能共享session存储空间,当请求切换到不同tomcat服务器时导致数据丢失。

  1. 发送验证码,Redis保存验证码。
  2. 校验验证码,用户信息保存到Redis中,返回token给前端
  3. 前端请求首部行中携带token(请求首部中添加Authorization字段)校验登录状态,判断Redis里是否有用户信息。

验证码:key是手机号,value是string类型,验证码。

用户信息:key是token,value是Hash类型,用户。

商户查询缓存

缓存,交换数据的缓冲区,存储数据的临时地方,读写性能好。

缓存的作用,降低后端数据库负载,提高读写效率降低响应时间

添加Redis缓存

  1. 商铺请求先访问Redis,Redis命中直接返回数据。
  2. Redis未命中,查询数据库,如果数据库中存在数据,再写入Redis中,否则返回404。

缓存更新策略:

  • 内存淘汰。内存淘汰自动删除部分数据。
  • 超时剔除。给缓存增加TTL,到期自动删除缓存。
  • 主动更新。先操作数据库,再删除缓存。

一致性也就是缓存中有值时,与数据库中一致。

低一致性需求:使用Redis自带的内存淘汰机制。

高一致性需求:使用主动更新策略,并以超时剔除为兜底。

主动更新:先删缓存再写数据库、先写数据库再删缓存、先写数据库再写缓存、读写穿透、写回。

读操作:

  • 缓存命中直接返回。
  • 缓存未命中则查询数据库,并写入缓存,设置TTL。

写操作:

  • 先写数据库,再删除缓存
  • 确保数据库与缓存的操作原子性。

先删缓存再写数据库的问题?

更新操作删除缓存后还没更新数据库时,查询操作缓存未命中读数据库旧值,更新操作更新缓存,查询操作写入缓存旧值。

读写穿透:读写缓存数据库同步服务。

写回:只更新缓存,异步更新数据库。

缓存穿透

客户端请求的数据在数据库和缓存中都不存在,缓存永远无法生效,请求会打到数据库上给数据库带来压力。

常见的解决方案:缓存空对象和布隆过滤器。

缓存空对象:在查询时缓存和数据库都未没命中,在缓存中缓存空值设置TTL。在查询缓存的时候判断是否是空值(不是nil)。

布隆过滤器:所有可能请求的值存放在布隆过滤器中,请求来时先判断是否存在,不存在直接返回错误信息。

缓存雪崩

同一时间大量的缓存key同时失效或者Redis服务宕机,大量请求到达数据库带来压力。

解决方案:

  • 设置随机失效时间。
  • 缓存预热。
  • Redis集群。
  • 多级缓存。
  • 业务添加多级缓存。

缓存击穿

热点key问题,被高并发访问并且缓存重建业务复杂的key突然失效,大量请求瞬间到达数据库带来压力。

解决方案:互斥锁和逻辑过期。

互斥锁方案

查询缓存未命中获取互斥锁,查询数据库重建缓存数据,写入缓存,释放锁。其他线程查询缓存未命中获取互斥锁失败,休眠重试直到缓存命中。

逻辑过期

缓存数据中添加逻辑TTL,查询缓存发现已过期,直接返回过期数据,获取互斥锁开启新线程重建缓存,写入缓存后重置逻辑TTL释放互斥锁。其他线程查询缓存发现已过期,获取互斥锁失败返回过期数据。

优惠券秒杀

全局ID生成器

分布式系统下生成全局唯一的ID工具。

基于Redis自增策略,订单ID由Redis中整型数据4个字节64位组成,1比特为0符号位,31位时间戳,32为序列号。使用Redis整型数据自增来生成ID。

秒杀券下单

实现优惠券秒杀:

  1. 判断秒杀是否开始或者结束
  2. 库存是否充足,扣减库存,添加订单

在高并发环境下,会出现超卖的问题(在库存量为1的时候,多个线程进入判断库存充足扣减订单,超卖订单)。

悲观锁:添加互斥锁,让线程串行执行,实现简单但性能一般。

乐观锁:不加锁,在更新时判断是否有其他线程在修改,性能好但成功率低。版本号法,在更新数据之前检查版本号,更新数据时修改版本号。

// 优化乐观锁成果率低的问题,在更新时同时检查订单是否大于0
seckillVoucherService.update().setSql("stock = stock - 1").eq("voucher_id", voucherId).gt("stock", 0).update();

一人一单

在更新前判断数据库订单表中是否相同用户相同秒杀券的记录,由于没有加锁同样会出现超卖的问题。

由于不存在数据更新,所以无法使用乐观锁,使用悲观锁synchronized给创建订单上锁。

一人一单的并发安全问题:在集群模式下,同一个用户在不同服务器上的请求会获取不同的锁(不同的JVM,不同的锁监视器),会超单。

分布式锁

满足分布式系统或集群模式下多进程可见并且互斥的锁。

基于Redis分布式锁

版本1:基于Redis的setnx命令实现分布式锁,设置TTL超时自动释放锁。

版本1的问题:线程1业务阻塞,锁TTL超时释放,线程2获取锁执行业务中,线程1执行完任务释放线程2的锁。

版本2:获取锁时存入线程标识(UUID+线程ID,不能只使用线程ID,不同JVM线程ID可能相同),释放锁时核对线程标识。

版本2的问题:线程1判断锁标识和释放锁之间阻塞,锁TTL超时释放,线程2获取锁执行业务前,线程1阻塞结束后释放锁。

版本3:使用Luna脚本完成判断线程标识和锁释放的原子性。

总结:基于Redis的分布式锁实现思路,利用setnx获取锁设置过期时间,保存线程标识。释放锁先判断线程标识是否一致,一致释放锁

基于Redis分布式锁优化

基于setnx实现的分布式锁存在的问题:

  1. 不可重入。同一个线程无法多次获取同一把锁。
  2. 不可重试。获取锁尝试一次就返回。
  3. 超时释放。如果业务执行时间耗时长,导致锁提前释放。
  4. 主从一致性。Redis采用主从集群结构, 主节点宕机且锁数据未同步给从节点,导致锁失效。

Redisson分布式锁工具。

可重入锁的原理:利用Hash结构记录线程标识和重入次数。

可重试的原理:利用pubsub机制,订阅锁释放的信号尝试获取锁。

锁不过期的原理:开启watchdog定时更新TTL。

主从一致性的原理:联锁,每台Redis服务器独立,获取锁时需要半数以上的每台服务器都获取锁。

秒杀优化

业务流程:

  1. 查询优惠券,判断秒杀库存。读数据库。
  2. 查询订单, 判断一人一单。读数据库。
  3. 更新库存。写数据库。
  4. 创建订单。写数据库。

优化方式,同步下单变成异步下单,读写数据库分离。利用Redis完成库存余量、一人一单判断,完成抢单业务。再将下单业务放入阻塞队列,利用独立线程异步下单。

基于阻塞队列的异步秒杀问题:内存限制,阻塞队列使用JVM内存,高并发情况下大量订单占用JVM内存。数据安全问题,服务重启或者宕机,阻塞队列数据丢失。

基于Redis的消息队列实现异步秒杀
  • 基于List双向链表实现。LPUSH和BRPOP阻塞获取。只支持单消费者,无法避免消息丢失
  • 基于PubSub实现。发布消息,订阅频道。不支持数据持久化,无法避免消息丢失
  • 基于Stream实现。多个消费者划分到一个消费者组,监听同一个队列。

Stream消费者组消息队列的特点:

  • 消息分流。同一个消费者组的消费者之间竞争消息。
  • 消息标识。消费者组维护一个标识,最后一个被处理的消息。
  • 消息确认。消费者获取消息后,消息处于pending状态,在pending-list中,当消息处理返回ack确定后才会从list中移除。

秒杀优化券方案总结:利用lua脚本完成库存余量、一人一单判断,完成抢单业务。将下单信息放到消息队列,开启独立线程异步下单,扣减库存使用乐观锁。

达人探店

点赞

在Redis中记录以blogid为key,点赞用户为value的集合。

点赞排行榜:使用SortedSet,点赞时带时间为score。

好友关注

关注

user_idfans_id多对多关系表中添加。

共同关注

为每个用户在redis添加一个关注集合set,求两个集合的交集。

关注推送

Feed流,无限下拉刷新获取新消息。

  • 拉模式:用户从关注邮箱中拉去消息。读,延迟高。
  • 推模式:发布消息到用户的邮箱中。写,延迟低。
  • 推拉结合:粉丝多的采用拉,粉丝少的采用推。

基于推模式实现关注推送:blog保存到数据库的同时,推送到粉丝的邮件箱,收件箱使用Redis的SortedSet满足时间戳排序,查询收件箱数据时实现分页查询(滚动式分页,数据不断变化,数据的角标也在变化。维护上一次最小的时间戳和偏移量)。

附近商户

Redis GEO结构存储地理位置信息。

根据商铺类型分组,typeid作为key存入同一个GEO集合中,成员是店铺ID。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/9549.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

安卓开发--按键背景图片,按键其他属性,按键按键按下变色

前面已经介绍了一个空白按键工程的建立以及响应方式,可以参考这里:安卓开发–新建工程,新建虚拟手机,按键事件响应。 安卓开发是页面跳转是基础!!!所以本篇博客介绍利用按键实现页面跳转&#…

添加对象方法

添加对象方法的方法如下,这是一个通用模式 注意灵活运用。

数据库调优-SQL语句优化

2. SQL语句优化 sql 复制代码 # 请问这两条SQL语句有什么区别呢?你来猜一猜那条SQL语句执行查询效果更好! select id from sys_goods where goods_name华为 HUAWEI 麦芒7 魅海蓝 6G64G 全网通; ​ select id from sys_goods where goods_id14967325985…

Redis学习1——redis简介、基础

介绍 redis简介 Redis(Remote Dictonary Server) 是由Salvatore Sanfilippo开发的key-value缓存数据库,基于C语言开发。目前市面上,Redis和MongoDB是当前使用最广泛的NoSQL,而就Redis技术而言,它的性能十分优越,可以…

仿真相关知识积累

融合模型是什么 集成学习 融合模型,也称为集成学习,是一种将多个基模型组合在一起以提升整体模型性能的方法。这种思想通过不同的策略将多个相对较弱的模型(学习器)结合起来,从而达到较强的模型性能。

rngd: Error writing /dev/tpm0

检查数据库时发现messages中一直有rngd报错,rngd一直未配置,直接关闭了 /var/log/messages-20240414:Apr 11 04:59:49 hydb2 rngd: Error writing /dev/tpm0 /var/log/messages-20240414:Apr 12 07:31:39 hydb2 rngd: Error writing /dev/tpm0 /var/log…

shell-for循环语句练习题

1.计算从1到100所有整数的和 [rootlocalhost ~]# vim 1.sh #!/bin/bash sum0 #定义变量sum初始值为0 for i in {1..100} #for循环 i取值从1到100 do sum$[ isum ] #在每次循环中,变量i的值会依次取1到100的整数值。 #sum是一个累加器,初始值…

echarts自定义图例显示名称、数值、占比

先上代码 legend: {orient: vertical,left: 10,top:20,data: data,textStyle: {color: #9FB7D5 // 设置图例文字颜色为白色},// type: plain, // 设置图例类型为普通类型itemWidth: 10, // 设置图例项的宽度itemHeight: 10, // 设置图例项的高度formatter: function(name) {let…

Python专题:十、字典(2)

字典定义x{} get()函数 get(参数一,参数二) 参数一: 需要查找的关键词 参数二: 如果关键词不存在get返回的默认值 字典的更新 update()函数,字典y的元素,去更新字…

学生管理系统初级

根据题目要求生成大纲 总结: 1.在书写时,考虑到了书写时id可是是abc... 类型是String,但在根据id获取集合中元素时 list.get() ,get()里面是int类型。 2.在书写还有一点功能并不完全, 2.1查找时是打印所有…

缓存雪崩、缓存击穿、缓存穿透是什么、之间的区别及解决办法

缓存雪崩、缓存击穿、缓存穿透: 详细介绍看这篇文章,写得很好: 什么是缓存雪崩、缓存击穿、缓存穿透 下面是我自己总结的,比较简单清楚地展示了缓存雪崩、缓存击穿和缓存穿透的根本区别和相应的解决办法。强烈建议看完上述文章后…

十.吊打面试官系列-Tomcat优化-通过压测Tomcat调优实战

前言 上一篇文章我们讲解了一下Tomcat底层的结构和执行原理,我们需要重点去掌握的是Tomcat的高内聚低耦合的设计,以及责任链模式,以及Tomcat NIO编程模式,这些是Tomcat比较核心的点,本篇文章我们将对Tomcat的参数做一…

Java学习【类与对象】

类和对象 开始我们就不讲那些把大象放冰箱需要几步来引入面向对象的例子了,直接上干货。 在Java中,类是对现实世界中某一类事物的抽象描述。它包含了该类事物的属性和方法。属性用于描述事物的状态,而方法则用于描述事物可以做的事情。对象也…

Redis 支持的 Java 客户端都有哪些?

Redis 是一种高性能的键值存储系统,它以其快速、灵活和可扩展的特性而闻名。在 Java 开发中,与 Redis 交互的方式通常是通过使用 Redis 的 Java 客户端。 这些客户端提供了访问 Redis 数据库的接口,使开发人员能够在 Java 应用程序中轻松地使…

MongoDB聚合运算符:$topN

MongoDB聚合运算符:$topN 文章目录 MongoDB聚合运算符:$topN语法用法关于null和缺失值的处理BSON数据类型排序 举例查找三个得分最高的查找全部游戏中三个最高的得分基于分组key来计算参数n $topN聚合运算符返回分组中指定顺序的最前面 n个元素&#xf…

使得安全团队难以准确评估网络的真实安全状况

防火墙策略管理与策略分析:如何准确评估网络真实安全状况 摘要 随着信息技术的飞速发展,网络安全问题日益严重。防火墙作为网络安全的第一道防线,对于保障网络的安全具有重要意义。然而,在实际应用中,由于防火墙策略管…

704. 二分查找

Problem: 704. 二分查找 🐷我的leetcode主页 文章目录 题目分类思路什么是二分查找如何理解时间复杂度 解题方法Code 题目 给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target&a…

电脑设置在哪里打开?Window与Mac双系统操作指南

随着科技的不断发展,电脑已经成为我们日常生活和工作中不可或缺的一部分。然而,对于许多初学者来说,如何找到并熟悉电脑的设置界面可能是一个挑战。特别是对于那些同时使用Windows和Mac双系统的用户来说,更是需要一篇详尽的指南来…

qml 和 c++类的数据交互

1、 新建一个需要交互的C++类 1)添加QObject头文件 2)添加自QObject的继承 3)添加Q_OBJECT宏 4)使用Q_PROPERTY,定义两个交互的属性,并设置读写的方法和变更属性的信号。 5)添加方法、槽函数和变量 2、在main.cpp中添加实例化对象的QML上下文 1)添加需要QML交互的…

【QT教程】QT6音视频性能优化技巧 QT音视频

QT6音视频性能优化技巧 使用AI技术辅助生成 QT界面美化视频课程 QT性能优化视频课程 QT原理与源码分析视频课程 QT QML C扩展开发视频课程 免费QT视频课程 您可以看免费1000个QT技术视频 免费QT视频课程 QT统计图和QT数据可视化视频免费看 免费QT视频课程 QT性能优化视频免费…