黑马点评redis使用 (1)
创始人
2025-05-30 14:05:42
0

搭建环境

黑马点评用的redis版本比较高,这里有win6.2.6版本安装包,否则会有报错
本人使用的各种开发工具都在网盘,需要自取
提取码:atox

拦截与注册

登录、注册使用了jwt单点登录

相关代码Demo都在giee仓库 ,前后端代码都在仓库
后端gitee
前端gitee

缓存相关

缓存概念

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

缓存更新策略:

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

缓存更新策略的最佳实践方案:

低一致性需求:使用Redis自带的内存淘汰机制
高一致性需求:主动更新,并以超时剔除作为兜底方案
读操作:

  • 缓存命中则直接返回
  • 缓存未命中则查询数据库,并写入缓存,设定超时时间

写操作:

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

缓存穿透

缓存穿透 是指客户端请求的数据在缓存中和数据库中都不存在,这样缓存永远不会生效,这些请求都会打到数据库。
在这里插入图片描述
在这里插入图片描述

缓存穿透产生的原因是什么?

用户请求的数据在缓存中和数据库中都不存在,不断发起这样的请求,给数据库带来巨大压力

缓存穿透的解决方案有哪些?

缓存null值
布隆过滤
增强id的复杂度,避免被猜测id规律
做好数据的基础格式校验
加强用户权限校验
做好热点参数的限流

缓存雪崩

缓存雪崩是指在同一时段大量的缓存key同时失效或者Redis服务宕机,导致大量请求到达数据库,带来巨大压力。

解决方案:

给不同的Key的TTL添加随机值
利用Redis集群提高服务的可用性
给缓存业务添加降级限流策略
给业务添加多级缓存
在这里插入图片描述

缓存击穿

缓存击穿问题也叫热点Key问题,就是一个被高并发访问并且缓存重建业务较复杂的key突然失效了,无数的请求访问会在瞬间给数据库带来巨大的冲击。
常见的解决方案有两种:

  • 互斥锁
  • 逻辑过期

在这里插入图片描述

解决方案优点缺点
互斥锁没有额外的内存消耗,保证一致性,实现简单线程需要等待,性能受影响,可能有死锁风险
逻辑过期线程无需等待,性能较好不保证一致性,有额外内存消耗,实现复杂

在这里插入图片描述

在这里插入图片描述

缓存工具封装

基于StringRedisTemplate封装一个缓存工具类,满足下列需求:

方法1:将任意Java对象序列化为json并存储在string类型的key中,并且可以设置TTL过期时间
方法2:将任意Java对象序列化为json并存储在string类型的key中,并且可以设置逻辑过期时间,用于处理缓存击穿问题
方法3:根据指定的key查询缓存,并反序列化为指定类型,利用缓存空值的方式解决缓存穿透问题
方法4:根据指定的key查询缓存,并反序列化为指定类型,需要利用逻辑过期解决缓存击穿问题

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 lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;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_NULL_TTL;
import static com.hmdp.utils.RedisConstants.LOCK_SHOP_KEY;@Slf4j
@Component
public class CacheClient {private final StringRedisTemplate stringRedisTemplate;private static final ExecutorService CACHE_REBUILD_EXECUTOR = Executors.newFixedThreadPool(10);public CacheClient(StringRedisTemplate stringRedisTemplate) {this.stringRedisTemplate = stringRedisTemplate;}public void set(String key, Object value, Long time, TimeUnit unit) {stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(value), time, unit);}public void setWithLogicalExpire(String key, Object value, Long time, TimeUnit unit) {// 设置逻辑过期RedisData redisData = new RedisData();redisData.setData(value);redisData.setExpireTime(LocalDateTime.now().plusSeconds(unit.toSeconds(time)));// 写入RedisstringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(redisData));}// 缓存穿透public  R queryWithPassThrough(String keyPrefix, ID id, Class type, Function dbFallback, Long time, TimeUnit unit){String key = keyPrefix + id;// 1.从redis查询商铺缓存String json = stringRedisTemplate.opsForValue().get(key);// 2.判断是否存在if (StrUtil.isNotBlank(json)) {// 3.存在,直接返回return JSONUtil.toBean(json, type);}// 判断命中的是否是空值if (json != null) {// 返回一个错误信息return null;}// 4.不存在,根据id查询数据库R r = dbFallback.apply(id);// 5.不存在,返回错误if (r == null) {// 将空值写入redisstringRedisTemplate.opsForValue().set(key, "", CACHE_NULL_TTL, TimeUnit.MINUTES);// 返回错误信息return null;}// 6.存在,写入redisthis.set(key, r, time, unit);return r;}/*** @author hlf* @date 2023/3/18 22:05* @description 逻辑过期解决缓存击穿**/public  R queryWithLogicalExpire(String keyPrefix, ID id, Class type, Function dbFallback, Long time, TimeUnit unit) {String key = keyPrefix + id;// 1.从redis查询商铺缓存String json = stringRedisTemplate.opsForValue().get(key);// 2.判断是否存在if (StrUtil.isBlank(json)) {// 3.存在,直接返回return null;}// 4.命中,需要先把json反序列化为对象RedisData redisData = JSONUtil.toBean(json, RedisData.class);R r = JSONUtil.toBean((JSONObject) redisData.getData(), type);LocalDateTime expireTime = redisData.getExpireTime();// 5.判断是否过期if(expireTime.isAfter(LocalDateTime.now())) {// 5.1.未过期,直接返回店铺信息return r;}// 5.2.已过期,需要缓存重建// 6.缓存重建// 6.1.获取互斥锁String lockKey = LOCK_SHOP_KEY + id;boolean isLock = tryLock(lockKey);// 6.2.判断是否获取锁成功if (isLock){// 6.3.成功,开启独立线程,实现缓存重建CACHE_REBUILD_EXECUTOR.submit(() -> {try {// 查询数据库R newR = dbFallback.apply(id);// 重建缓存this.setWithLogicalExpire(key, newR, time, unit);} catch (Exception e) {throw new RuntimeException(e);}finally {// 释放锁unlock(lockKey);}});}// 6.4.返回过期的商铺信息return r;}/** @author hlf* @date 2023/3/18 22:06* @description 互斥锁解决缓存击穿**/public  R queryWithMutex(String keyPrefix, ID id, Class type, Function dbFallback, Long time, TimeUnit unit) {String key = keyPrefix + id;// 1.从redis查询商铺缓存String shopJson = stringRedisTemplate.opsForValue().get(key);// 2.判断是否存在if (StrUtil.isNotBlank(shopJson)) {// 3.存在,直接返回return JSONUtil.toBean(shopJson, type);}// 判断命中的是否是空值if (shopJson != null) {// 返回一个错误信息return null;}// 4.实现缓存重建// 4.1.获取互斥锁String lockKey = LOCK_SHOP_KEY + id;R r = null;try {boolean isLock = tryLock(lockKey);// 4.2.判断是否获取成功if (!isLock) {// 4.3.获取锁失败,休眠并重试Thread.sleep(50);return queryWithMutex(keyPrefix, id, type, dbFallback, time, unit);}// 4.4.获取锁成功,根据id查询数据库r = dbFallback.apply(id);// 5.不存在,返回错误if (r == null) {// 将空值写入redisstringRedisTemplate.opsForValue().set(key, "", CACHE_NULL_TTL, TimeUnit.MINUTES);// 返回错误信息return null;}// 6.存在,写入redisthis.set(key, r, time, unit);} catch (InterruptedException e) {throw new RuntimeException(e);}finally {// 7.释放锁unlock(lockKey);}// 8.返回return r;}private boolean tryLock(String key) {Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(key, "1", 10, TimeUnit.SECONDS);return BooleanUtil.isTrue(flag);}private void unlock(String key) {stringRedisTemplate.delete(key);}
}

全局ID生成器

类似于雪花算法

在这里插入图片描述
全局唯一ID生成策略:

  • List item

  • UUID

  • Redis自增

  • snowflake算法

  • 数据库自增

Redis自增ID策略:

  • 每天一个key,方便统计订单量
  • ID构造是 时间戳 + 计数器
package com.hmdp.utils;import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;@Component
public class RedisIdWorker {/*** 开始时间戳*/private static final long BEGIN_TIMESTAMP = 1640995200L;/*** 序列号的位数*/private static final int COUNT_BITS = 32;private StringRedisTemplate stringRedisTemplate;public RedisIdWorker(StringRedisTemplate stringRedisTemplate) {this.stringRedisTemplate = stringRedisTemplate;}public long nextId(String keyPrefix) {// 1.生成时间戳LocalDateTime now = LocalDateTime.now();long nowSecond = now.toEpochSecond(ZoneOffset.UTC);long timestamp = nowSecond - BEGIN_TIMESTAMP;// 2.生成序列号// 2.1.获取当前日期,精确到天String date = now.format(DateTimeFormatter.ofPattern("yyyy:MM:dd"));// 2.2.自增长long count = stringRedisTemplate.opsForValue().increment("icr:" + keyPrefix + ":" + date);// 3.拼接并返回return timestamp << COUNT_BITS | count;}
}

使用测试类测试

 @Testvoid testIdWorker() throws InterruptedException {CountDownLatch latch = new CountDownLatch(300);Runnable task = () -> {for (int i = 0; i < 100; i++) {long id = redisIdWorker.nextId("order");System.out.println("id = " + id);}latch.countDown();};long begin = System.currentTimeMillis();for (int i = 0; i < 300; i++) { // 跑300次es.submit(task);}latch.await();long end = System.currentTimeMillis();System.out.println("time = " + (end - begin));}

在这里插入图片描述

未完待续。。。

相关内容

热门资讯

b01lers CTF web... warmup 按照提示依次 base64 加密后访问,可以访问 ./flag.txt&...
SSM项目 创建一个vue工程:cmd: vue create 项目名npm run serve:...
牧原股份回应赴港上市原因:意在... 新京报贝壳财经讯(记者阎侠)5月30日,牧原股份发布投资者关系活动记录表。今年4月,牧原股份的生猪养...
I. 全球变暖(23分)【df... 具体出处蓝桥杯2018年cpp组【第九届】【省赛】【B组】P8662 [蓝桥杯 2018 省 AB]...
三十四、实战演练之接口自动化平... 一、 环境创建 接口名称:/test_envs/ 请求方式:POST 参...
生成式AI为百度打开想象力 出品 | 何玺 排版 | 叶媛 3月16日,备受期待的百度人工智能系统文心一言正式发布...
javaweb实验室学生考勤签... 管理员信息表,包括自动编号,管理员账号,登录密码等数据字段...
提供一种嵌套表单的校验姿势 文章目录一、表单校验1. 代码演示 一、表单校验 表单校验,前端经常遇到的校验场景&...
go语言入门-一文带你掌握go... 前言 本文go语言入门-掌握go语言函数收录于《go语言学习专栏》专栏,此专栏带你从零...
【Vue3】tinymce富文... 1.简介 TinyMCE是一款易用、且功能强大的所见即所得的富文本编辑器。同类程序有:...
量子计算(10)量子算法2:D...         又到了一周一篇的量子计算啦!全体起立respect! ...
十六、FreeRTOS中如何实... 文章目录1、多任务系统中为什么要引入互斥?2、如何实现互斥访问的3、需要互斥访问内核对...
如何把自有数据接入GPT大模型... ChatGPT引发了AI革命,大家都想探究如何让它发挥更大价值。 以它为代表的大模型并...
美国中重卡销量10强:福莱纳领... 陌生的美国中重卡市场在我们的一般印象中,全球各国的汽车市场互联互通,应该是大同小异的。无论是是美国、...
Android WebView... 1、不使用WebView缓存 使用场景:通过WebView输入用户名和密码进行登录&#...
阿里云PAI-DeepRec ... 阿里云联合英特尔举办的“创新大师杯”全球AI极客挑战赛——PAI-DeepRec CTR模型性能优化...
拒绝转保!34年编外员工办不了... 临近退休了,突然被强制“自愿转保”,河北井陉县税务局的冯爱文拒绝签字,导致办理不了退休,到底发生了什...
中原证券:A股6月有望延续结构... 展望A股6月行情,中原证券在最新研报中表示,总体而言,6月A股在政策托底与盈利修复的共振下,有望延续...
【C++】类和对象三大特性--... 文章目录1. 多态的基本概念2. 多态的定义及实现2.1多态的构成条件2.2 虚函数2.3虚函数的重...
潜在因子模型(Latent F... LatentFactorModelsLatent Factor ModelsLatentFactor...