黑马点评(更新中)

news2024/11/24 12:55:41

黑马点评

  • 1、短信登录 Session实现
    • 1.1 分析
    • 1.2、Session实现的缺点
    • 1.3、其中的问题
      • 1.3.1、session覆盖
      • 1.3.2、在拦截之后remove User的作用
  • 2、用Redis实现短信登录
    • 2.1 分析
    • 2.2 代码以及问题
      • 2.2.1 String问题
      • 2.2.2 刷新问题
      • 2.2.3 注入对象问题
      • 2.2.4 拦截器order问题
  • 3、缓存
    • 3.1 商户信息的redis保存
    • 3.2 redis 商户列表redis缓存
    • 3.3 缓存更新策略
        • 实践 商户信息一致性
    • 3.4 缓存穿透
      • 利用缓存空对象来解决缓存穿透问题
    • 3.5 缓存雪崩
    • 3.6 缓存击穿
      • 需求:修改根据id查询商铺的业务,基于互斥锁方式来解决缓存击穿问题
      • 需求:修改根据id查询商铺的业务,基于逻辑过期时间方式来解决缓存击穿问题
    • 3.7 工具类封装
  • 4、优惠券秒杀
    • 4.1 全局唯一ID
      • 4.1.1 id生成器的特性


1、短信登录 Session实现

1.1 分析

在这里插入图片描述
部分代码:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

1.2、Session实现的缺点

在这里插入图片描述
redis可以数据共享,且redis是内存存储,key-value结构。

1.3、其中的问题

1.3.1、session覆盖

看弹幕有人提到说如果多个用户登录,会不会出现session覆盖问题,都保存成属性值一样的情况下
回答 :不会,因为请求到服务器中,是基于session-id保存的,每个session-id的属性值相同不会有影响,是基于用户的。

1.3.2、在拦截之后remove User的作用

防止内存泄漏,如果当前线程仍然使用,但是却不在使用theardLocal,那么如果我们没有remove的话,即使key是弱引用,theadLocal 为null,但是key仍然有值,所以最好的办法就是手动remove。

2、用Redis实现短信登录

2.1 分析

在这里插入图片描述

使用redis着重要考虑:
①键要唯一
②如果存储键,方便我们后续的读取。

在这里,保存code时,首先数据类型使用的是String,在 redis中使用phone,刚好登录时,会提交phone和code,根据提交的code可以在redis中查找到code,然后验证。

保存用户信息时,使用的数据类型是Hash,Hash可以存储对象,当然也可以使用String 的json字符串进行保存。key这里使用的是一个随机字符串,那后续如何获取呢?我们可以手动把其放在token中,前端获取后端返回的数据保存token中,接着,每次发送ajax请求时,都会携带到请求头中,key为authorization。
在这里插入图片描述

在这里插入图片描述

2.2 代码以及问题

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.2.1 String问题

id是Long型,但是BeanToMap的值是String,Object,并且我们使用的是StringRedisTemplate,这要求我们保存到redis中的值为String类型,这时候就需要我们显示的将值转为String。
在这里插入图片描述
基本类型与对象类型: Java 中的基本类型(如 int、long、boolean)和它们的包装类型(如 Integer、Long、Boolean)与 String 之间的转换需要明确的语法,因为 Java 是强类型语言,编译器不会自动执行这些转换。

2.2.2 刷新问题

我们保证一个用户的活跃度,这时候应该是只要其访问了网页就要刷新其时常,所以不应该只在login时刷新,也不应该只在登录校验的几个网页做刷新。而应该在在所有的请求时都做刷新,这时候需要再有一个拦截器。对所有网页都刷新,但是这个拦截器不做登录校验功能,而是如果获取到了用户,把用户信息保存在Local中,不管咋样,都放行,交给登录校验的拦截器。

2.2.3 注入对象问题

使用 new 创建的对象不能通过 Spring 的 @Resource、@Autowired 或其他依赖注入注解进行注入。这是因为 Spring 的依赖注入机制依赖于容器管理的 Bean。通过 new 创建的对象不在 Spring 容器的管理范围内,因此无法享受到容器提供的特性(如生命周期管理、AOP、事务管理等)。但我们可以通过构造函数来注入。

2.2.4 拦截器order问题

哪个先注册在前面先执行哪一个,默认order都是0.可以通过order来决定先执行哪一个,值越小优先级越高。

3、缓存

在这里插入图片描述

3.1 商户信息的redis保存

使用了String
在这里插入图片描述

3.2 redis 商户列表redis缓存

使用了List
在这里插入图片描述

3.3 缓存更新策略

在这里插入图片描述
主动更新时 我们都选择自主更新就是由代码开发者 自己确定更新缓存
在这里插入图片描述
在这里插入图片描述
数据库数据变更时,我们选择删除缓存,而不是更新缓存,删除缓存的好处:数据库数据变更时,我们直接删除缓存,比如更新100次,只用删除一次就行了,查询时在更新缓存,但是如果更新缓存的话,就是导致,数据库数据变更100次那么缓存就要更新100次,如果中间没有读操作,就会导致无效的写操作。
在这里插入图片描述
我们一般都会选择先操作数据库在删除缓存。
第一种情况:先删除缓存,再操作数据库这种误差的机率比较大,因为更新数据库的操作慢,但是写缓存的速度快,在多线程的情况下,这种出现的概率比较大。
但是第二种情况:先操作数据,在删除缓存,由于更新数据库的速度比较慢,所以一般不会这样子进行,并且,即使写入缓存导致不一致了,也可以加入超时策略,作为兜底方案。
在这里插入图片描述

实践 商户信息一致性

查询操作时,添加过期时间
在这里插入图片描述
更新操作时
在这里插入图片描述

3.4 缓存穿透

在这里插入图片描述
查询商铺时的信息修改逻辑
当数据库不存在时,则把其存入到redis当中。存一个空字符串。
查询到时 如果是空值 则直接结束。
记得设置一个过期时间,因为在这中间可能会在数据库中插入信息。

在这里插入图片描述

利用缓存空对象来解决缓存穿透问题

在这里插入图片描述

3.5 缓存雪崩

在这里插入图片描述

3.6 缓存击穿

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

需求:修改根据id查询商铺的业务,基于互斥锁方式来解决缓存击穿问题

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

需求:修改根据id查询商铺的业务,基于逻辑过期时间方式来解决缓存击穿问题

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.7 工具类封装

如果每个都需要我们去写缓存击穿缓存穿透的话就很麻烦,我们可以封装一个工具类

在这里插入图片描述
工具类

package com.hmdp.utils;

import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.hmdp.entity.Shop;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;

import static com.hmdp.utils.RedisConstants.CACHE_SHOP_KEY;
import static com.hmdp.utils.RedisConstants.LOCK_SHOP_KEY;

/**
 * @ClassName ChcheClicnt
 * @Description TODO
 * @Author lukcy
 * @Date 2024/10/10 17:16
 * @Version 1.0
 */
@Slf4j
@Component
public class ChcheClicnt {
    @Resource
    private StringRedisTemplate stringRedisTemplate;

    public  void  set(String key, Object value, Long time, TimeUnit unit){
        stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(value),time,unit);
    }
    private static final ExecutorService CACHAE_REBUILD_EXECUTOR= Executors.newFixedThreadPool(10);
    public void setWithLogicExpire(String key, Object value, Long time, TimeUnit unit){
        RedisData redisData=new RedisData();
        redisData.setData(value);
        redisData.setExpireTime(LocalDateTime.now().plusSeconds(unit.toSeconds(time)));
        stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(redisData));
    }

    public  <R,ID> R queryWithPassThough(String prifix, ID id, Class<R> type, Function<ID,R> DBcallback,Long time, TimeUnit unit){
        String key=prifix+id;
        //查询redis中是否有
        String Json = stringRedisTemplate.opsForValue().get(key);
        //有直接返回
        if (StrUtil.isNotBlank(Json)) {
            R r = JSONUtil.toBean(Json, type);
            return r;
        }
        //redis中保存的是空值
        if(Json!=null){
            return null;
        }
        //没有 查询数据库
        R r = DBcallback.apply(id);

        //数据库中没有 保存空值设置短的过期时间 返回错误
        if (r == null) {
            stringRedisTemplate.opsForValue().set(key,"",2L, TimeUnit.MINUTES);
            return null;
        }
        //有,保存Json形式在redis中 设置过期时间
        this.set(key,r,time,unit);
        //返回
        return r;
    }

    public <R,ID> R queryWithExpire(String prifix, ID id, Class<R> type, Function<ID,R> DBcallback,Long time, TimeUnit unit){
        String key=prifix+id;
        //查询redis中是否有
        String Json = stringRedisTemplate.opsForValue().get(key);
        //没有 直接返回
        if (StrUtil.isBlank(Json)) {
            return null;
        }
        //有 判断是否过期 把json反序列化为对象
        RedisData redisData = JSONUtil.toBean(Json, RedisData.class);
        LocalDateTime expireTime = redisData.getExpireTime();
        R r = JSONUtil.toBean((JSONObject) redisData.getData(), type);
        //没有过期
        if(expireTime.isAfter(LocalDateTime.now())){
            return r;
        }
        //过期
        String lockkey=LOCK_SHOP_KEY+id;
        // 尝试获取锁
        if (!tryLock(lockkey)) {
            //获取锁失败
            return r;
        }
        //获取锁成功
        CACHAE_REBUILD_EXECUTOR.submit(()->{
            //开启线程 重建缓存
            //查数据库
            try {
            R r1 = DBcallback.apply(id);
            //存入缓存
            this.setWithLogicExpire(key,r1,time,unit);}
          catch (Exception e) {
                e.printStackTrace();
            } finally {
                deleteLock(lockkey);
            }
        });
        //返回
        return r;
    }
    private boolean tryLock(String key){
        Boolean aBoolean = stringRedisTemplate.opsForValue().setIfAbsent(key, "1", 10, TimeUnit.SECONDS);
        return BooleanUtil.isTrue(aBoolean);

    }
    private void deleteLock(String key){
        stringRedisTemplate.delete(key);


    }
}

运用

  public Result queryById(Long id) {
        //缓存穿透 利用缓存空值解决
//        Shop shop=chcheClicnt.queryWithPassThough(CACHE_SHOP_KEY,id,Shop.class,this::getById,CACHE_SHOP_TTL,TimeUnit.MINUTES);
        //缓存击穿 利用互斥锁解决
//        Shop shop = queryWithMeutx(id);
        Shop shop=chcheClicnt.queryWithExpire(CACHE_SHOP_KEY,id,Shop.class,this::getById,CACHE_SHOP_TTL,TimeUnit.MINUTES);
        if(shop==null){
            return Result.fail("该商铺不存在!");
        }
        return Result.ok(shop);
    }

4、优惠券秒杀

4.1 全局唯一ID

在这里插入图片描述
用数据库自增长的话,规律太明显,并且一个系统,生成订单的量会日益增多,一个数据库容量可能不够,如果分到多个表中,由于运用的是数据库的自增长,可能会导致id相同。

4.1.1 id生成器的特性

在这里插入图片描述
在这里插入图片描述
我i们不直接使用redis的自增长,需要拼接一些其他信息,比如时间戳,这需要我们有一个基础的开始时间,还有序列号。
时间戳的话即使是一秒下单的,后续的序列号也有32位,一秒下单量是足够的。
还有一个问题就是reids自增的key,如果我们用的是一个key,即使不同业务用的key不同, 但是相同业务用一个key的话,也会一直自增,redis自增也是有上限的,64位,可能会超。因此我们使用的key精确到天,这样也方便我们统计每天的下单量。
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

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

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

相关文章

6个设计师都在用的样机素材网站

设计师都在哪里找样机素材&#xff1f;推荐6个样机素材网站&#xff0c;免费下载&#xff0c;赶紧收藏好&#xff01; 1、菜鸟图库 样机图片素材-样机图片模板免费下载 - 菜鸟图库 菜鸟图库有多种类型的设计素材&#xff0c;像平面、电商、UI、办公等素材这里面都能找到&#…

若依-二级页面的跳转设计

配置二级路由页面&#xff0c;就是点击了某个按钮之后会跳转到一个页面中去&#xff0c;比如需要点击查看详情的时候就可以进行页面的跳转。 点击字典类型 进入到字典数据页面。这个页面在左侧是没有的&#xff0c;在导航栏会有展示出来。 在index.js中配置的代码 //path 当前的…

从新手到专家,Tableau Agent 如何满足不同用户的分析需求?

为什么要构建 Tableau Agent&#xff1f;Tableau 对话式 AI 助理如何助你加速分析&#xff1f; 正在查询相关数据和现有仪表板&#xff0c;看看能否解答业务问题&#xff1f; 还在持续准备、管理和编辑数据&#xff0c;以确保企业数据的质量和可访问性&#xff1f; 希望快速创…

SDH8323非隔离12V/15V/18V,300MA直插DIP7电源芯片

SDH8323 是高压启动&#xff0c;内置高压MOSFET的电流模式PWMPFM控制器&#xff0c;适用于Buck及Buck-Boost拓扑。 SDH8323 在轻载条件下降频工作&#xff0c;优化轻载条件下的转换效率。在极轻载及空载条件下工作于打嗝模式&#xff0c;从而有效地降低系统的待机功耗。 SDH832…

DIFY上使用多种大语言模型(MindCraft API)

注册MindCraft并创建API KEY 首先我们在智匠MindCraft上注册账号并创建API KEY&#xff0c;参考接口调用文档&#xff0c;查看我们能调用哪些模型。我们可以看到这个开发平台上整合了主流的大语言模型&#xff0c;并且是兼容openai接口的。 进入DIFY的设置界面 然后我们在DIFY上…

为何一个简单的线上商城 两年销售7000多万?

有一个销售百货的商城&#xff0c;他们的返现模式极为独特且富有吸引力。无论你消费多少&#xff0c;商城都会随机给你返还一定金额的钱&#xff0c;这个返还范围从10元到1000元不等&#xff0c;确实非常随意。 那么&#xff0c;这个商城采用如此随性的返现模式&#xff0c;业绩…

软件性能测试有哪些测试指标?性能测试第三方软件测评中心推荐

作为衡量软件质量的重要指标之一&#xff0c;软件的性能是一种非功能特性&#xff0c;不关心系统是否可以完成特定的功能&#xff0c;而只关心软件系统在运行时的速度是否足够快、是否消耗足够少的资源&#xff0c;因此软件性能测试至关重要。性能测试是指软件测试人员根据产品…

监控台操作台在哪些企业中应用比较广泛

在现代企业管理中&#xff0c;监控台操作台作为一种集成了视频监控、音频监听、数据分析及远程控制等多种功能的综合性操作平台&#xff0c;正逐渐成为众多企业不可或缺的重要设备。其广泛的应用领域不仅提升了企业的运营效率&#xff0c;还极大地增强了企业的安全性能。 一、安…

淘宝程序员没活硬整?在 Excel 和 VSCode 中购物!

大家好&#xff0c;我是程序员鱼皮&#xff0c;最近某宝网站的改进&#xff0c;属实是有点 “新” 了。 你敢相信这是一个购物网站么&#xff1f; 你可以在 Excel 表格中挑选商品进行购物&#xff0c;还原度极高&#xff0c;这两个图表更是点睛之笔。哪个天才想出来的&#xf…

C++Linux项目推荐-Web多人聊天+MySQL+Redis+Websocket+Json,可以写简历的C++项目

1 项目地址 项目配套视频简介&#xff1a;程序员老廖的个人空间-程序员老廖个人主页-哔哩哔哩视频 (bilibili.com) 1.1 项目原有功能 https://github.com/anarthal/servertech-chat.git 功能&#xff1a; 支持HTTP请求&#xff0c;掌握HTTP API json的请求相应 支持Webso…

如何使用ssm实现疫情居家办公OA系统

TOC 10902ssm疫情居家办公OA系统 系统概述 进过系统的分析后&#xff0c;就开始记性系统的设计&#xff0c;系统设计包含总体设计和详细设计。总体设计只是一个大体的设计&#xff0c;经过了总体设计&#xff0c;我们能够划分出系统的一些东西&#xff0c;例如文件、文档、数…

手机怎么玩荒野大镖客2?GameViewer远程助你手机随时随地畅玩大表哥2

手机免费玩电脑游戏&#xff0c;原来手机也能随时随地玩荒野大镖客2&#xff1f;如果你想你手机随时随地畅玩大表哥2&#xff0c;可以使用网易GameViewer远程来实现。 GameViewer远程4K蓝光144帧的高画质&#xff0c;可以让你在玩荒野大镖客2时&#xff0c;体验到开放大世界的所…

2024年11月软考准考证什么时候打印?

打印时间一览表&#xff1a; 北京&#xff1a;2024年11月5日至11月8日 上海&#xff1a;2024年11月6日10:00至11月8日16:00 天津&#xff1a;2024年11月5日9:00后 重庆&#xff1a;2024年11月4日9:00至10日9:05 广东&#xff1a;2024年11月5日9:00至8日17:00 深圳&#xf…

靠“代工+营销”支撑,又突击分红10亿元,毛戈平冲刺上市为哪般

撰稿|行星 来源|贝多财经 10月9日&#xff0c;毛戈平化妆品股份有限公司&#xff08;下称“毛戈平”或“毛戈平公司”&#xff09;在港交所更新招股书&#xff0c;继续推进港股上市进程。此前&#xff0c;毛戈平曾于4月8日向港交所递表&#xff0c;但因财务资料已过有效期而“…

selenium:Select类操作复选框和下拉框(7)

复选框/下拉框操作的Select类 主要使用selinium中的类Select来模拟选择网页上的下拉框或者复选框中的内容&#xff0c;使用前先导入 from selenium.webdriver.support.ui import Select 主要方法如下&#xff1a; 函数 功能 select_by_value 根据复选框/下拉框的值选择 se…

openfeign解释及其应用

文章目录 前言一、FeignClient详解注解使用范围注解属性说明value()name()contextId()qualifiers()configurationfallbackfallbackFactorypath 二、openfeign走网关gateway 前言 本文讨论的是springcloud分布式微服务架构下&#xff0c;如何让openfeign请求也走gateway网关 本…

浏览器服务端文件下载控制(安全阻止、文件浏览器打开还是下载行为控制)

文章目录 简介Chrome已阻止不安全内容下载PDF直接打开txt、xml、js文件被自动打开了而不是下载阿里OSS设置response header阿里OSS修改metadata 简介 随着浏览器的发展&#xff0c;有很多安全方面的限制&#xff0c;对我们的文件下载行为产生了很大的影响。 在JavaScript下载…

鸿蒙OS投票机制

(基于openharmony5.0) 投票机制 param get | grep ohos.boot.time 图 投票机制参数图 只有当所有的投票完成&#xff0c;开机动画才会退出&#xff0c;整理需要投票的系统应用&#xff08;三方应用不参与投票&#xff09;如下图所示&#xff1a; 以进程foundation为例&…

pytorh学习笔记——波士顿房价预测

机器学习的“hello world”&#xff1a;波士顿房价预测 波士顿房价预测的背景不用提了&#xff0c;简单了解一下数据集的结构。 波士顿房价的数据集&#xff0c;共有506组数据&#xff0c;每组数据共14项&#xff0c;前13项是影响房价的各种因素&#xff0c;比如&…

深入探究d3d9.dll文件:从d3d9.dll文件丢失的原因到解决方案

在使用电脑的过程中&#xff0c;你是否突然遇到过这样令人头疼的情况&#xff1a;当你试图打开某个游戏或者特定的图形软件时&#xff0c;屏幕上弹出一个恼人的错误提示框&#xff0c;上面赫然写着“d3d9.dll文件丢失”。这个看似小小的文件缺失&#xff0c;却可能像一道无法逾…