黑马点评项目笔记

news/2024/5/21 3:10:48

短信登录

基于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.mrgr.cn/p/86477841

相关文章

kafka的名词解释

1.Replica(副本):在 Kafka 中,每个分区都有多个副本,用于提供数据的冗余备份和高可用性。副本可以分为两种类型:领导者副本(leader replica)和追随者副本(follower replica)。 领导者副本:每个分区都有一个领导者副本,它负责处理与客户端的所有读写请求,是分区的主…

vue3打开页面后文本框自动获得焦点

字符串写法 <script setup> import { ref, onMounted } from vue import ./index.cssconst input ref(null)onMounted(() > {input.value.focus() }) </script><template><div class"m-home-wrap"><input ref"input" />…

FPGA+炬力ARM实现VR视频播放器方案,3D眼镜显示

3D眼镜显示&#xff1a; FPGA炬力ARM方案&#xff0c;单个视频源信号&#xff0c;同时驱动两个LCD屏显示&#xff0c;实现3D 沉浸式播放 客户应用&#xff1a;VR视频播放器 主要功能&#xff1a; 1.支持多种格式视频文件播放 2.支持2D/3D 效果实时切换播放 3.支持TF卡/U盘文…

亚信安慧AntDB:解锁数智化的新时代

亚信安慧AntDB的融合实时的特性使得它在数据库领域独树一帜。传统的数据库系统往往只能追求数据的准确性和一致性&#xff0c;但在实际的业务场景中&#xff0c;这些特性并不能满足企业的需求。AntDB的出现打破了传统束缚&#xff0c;为企业带来了全新的数据处理方式&#xff0…

计算机基础-网络

一、网络设备 a.网卡(无线、有线) 物理层:提供物理地址,也叫MAC地址 b.网线(双绞线)、光缆 c.交换机、集线器 arp链路层:提供局域网内计算机与路由器之间建立端口映射,mac与IP的链路绑定 d.路由器 网络层:为局域网内计算机分配IP地址、防火墙、上网限制、网速控制登录 …

压力测试

压力测试压力测试 本文来自博客园,作者:{咏南中间件},转载请注明原文链接:https://www.cnblogs.com/hnxxcxg/p/18184743

预约咨询小程序源码搭建/部署/上线/运营/售后/更新

包含在线咨询、视频咨询、电话咨询、面询多种咨询方式&#xff0c;适用于心理、法律、宠物等预约咨询问诊场景 分类预览&#xff1a;小程序提供清晰的分类选项&#xff0c;使用户能够迅速找到所需的咨询服务类型&#xff0c;如法律咨询、心理咨询、医疗咨询等。预约时间选择&a…

实验四

一、实验题目 :代码审查 二、实验目的 1、熟悉编码风格,利用开发环境所提供的平台工具对代码进行自动格式审查; 2、根据代码规范制定代码走查表,并按所制定的审查规范互审代码。 三、实验内容 1、IDEA环境和PyCharm环境二选一; IDEA环境 (1)预先准备在IDEA环境下实现对输…

【redis学习】Redis-IO多路复用

为什么要有IO多路复用 大家印象中的redis都是单线程的,没有加锁的操作,因此才会是redis这么快的原因其中之一。先暂且不说redis究竟是不是单线程,即便是单线程的,作为服务提供方,面对成百上千的客户端连接请求,读写操作,单线程是怎么做到高效的处理这些请求?单线程处理…

h5使用js拉起微信支付

近期,业务需求对接了微信支付,做个总结。web网页想要拉起微信支付,有两种方法: H5下单支付 , JSAPI支付 。首先纯前端做不了微信支付,必须配合后端才能通过微信的下单请求。接下来说说这两种方法的区别。 H5微信下单支付 这种支付方式是用户在浏览器端打开网页,通过下单等…

Android studio 新版本 NewUI toolbar显示快捷按钮

新版本的Android studio 启用新的界面&#xff0c;以前许多快捷按键位置有变化 文章目录 设置始终显示主菜单设置ToolBar快捷按钮显示设置右下角显示分支 设置始终显示主菜单 原本要点击左上角几个横向才显示的菜单 设置始终显示&#xff0c;View -> Appearance -> Main…

C#开发的网络速度计 - 开源研究系列文章 - 个人小作品

上次发布了一个获取网络速度的例子( https://www.cnblogs.com/lzhdim/p/18167854 )&#xff0c;就是为了这次这个例子。用于在托盘里显示网络速度的图标&#xff0c;并且能够显示网络速度。下面就介绍一下这个小应用的源码。 1、 项目目录&#xff1b; 2、 源码介绍&#xff1b…

[muduo网络库]——muduo库的Reactor模型(剖析muduo网络库核心部分、设计思想)

一、前言 在学习 C 服务端的过程中&#xff0c;必不可少的一项就是熟悉一个网络库&#xff0c;包括网络库的应用和其底层实现。我们熟知的网络库有 libevent、libev、muduo、Netty 等&#xff0c;其中 muduo 是由陈硕大佬个人开发的 TCP 网络库&#xff0c;最近跟着课程正在深…

任天堂Switch全部记录

NS SWTICH大气层制作新的SD卡,解决部分大气层及固件问题。 原先的SD卡(128G)快全部放满游戏了,需要新的内存卡用于存放新的游戏。 有几张闲置的内存卡,可是容量最大只有64G, 无法通过直接全部复制+粘贴的办法,来启用新的小内存卡,涉及制作新的NS Switch SD卡有什么用 s…

用一个查询从销售订单直通供应商账单-NetSuite

朗读全文Your browser does not support the audio element. 有什么用 用一个查询从销售订单直通供应商账单 NetSuite: 从SO销售订单,到PO采购订单,到Bill供应商账单 下面文章的分享仅作为演示在SuiteQL Query Too中的展示,现实情况下的应用可以非常灵活(由于范围广泛…

ArrayList in C#

https://dotnettutorials.net/lesson/arraylist-collection-csharp/ c#中的数组列表是什么?c#中的ArrayList是一个非泛型集合类,它的工作方式类似于数组,但提供了动态调整大小、从集合中间添加和删除元素等功能。c#中的ArrayList可以用来添加未知数据,也就是说,当我们不知…

【JavaEE 初阶(四)】多线程进阶

❣博主主页: 33的博客❣ ▶️文章专栏分类:JavaEE◀️ &#x1f69a;我的代码仓库: 33的代码仓库&#x1f69a; &#x1faf5;&#x1faf5;&#x1faf5;关注我带你了解更多线程知识 目录 1.前言2.常见的锁策略2.1悲观锁vs乐观锁2.2轻量级锁vs重量级锁2.3自旋锁vs挂起锁2.4读写…

bzoj4399: 魔法少女LJJ

先上头图:诈骗题认真读题 c<=7 只需要考虑前七个操作 一.动态开点即可 二.线段树合并 三.四.对于这两个操作,可以先统计出有多少个数小于/大于x,然后删除所有小于/大于x的数,并在x位置加上这些数 五.下放标记查询即可 六.每个数最大为1e9,直接乘肯定会炸,所以可以用do…

unity基础(一)

内容概要&#xff1a; 生命周期函数vector3 位置 方向 缩放旋转等信息Vector3欧拉角和Quaternion四元素unity脚本执行顺序设置 一 生命周期函数 方法说明Awake最早调用,所以一般可以再此实现单例模式OnEnable组件激活后调用,在Awake后会调用一次Start在Update之前调用一次&a…

信息安全技术-分析题【太原理工大学】

没有历年题或明确说明大题会考什么&#xff0c;以下为个人猜测 简答题和选择判断占60&#xff0c;认真看题库和总结&#xff0c;能过d(^_^o) 好像说是加解密这类题会给公式让你直接套&#xff0c;但还是看一下基本原理&#xff0c;要不到时候蒙圈 1.加密算法步骤 图 1 是一个采…