redis核心面试题二(实战优化)

文章目录

  • 10. redis配置mysql实战优化[重要]
  • 11. redis之缓存击穿、缓存穿透、缓存雪崩
  • 12. redis实现分布式session

10. redis配置mysql实战优化[重要]

// 最初实现@Override@Transactionalpublic Product createProduct(Product product) {productRepo.saveAndFlush(product);jedis.set(SystemConstants.REDIS_KEY_PREFIX + product.getProductId(), gson.toJson(product))return product;}@Override@Transactionalpublic Product updateProduct(Product product) {productRepo.saveAndFlush(product);jedis.set(SystemConstants.REDIS_KEY_PREFIX + product.getProductId(), gson.toJson(product))return product;}@Overridepublic Product getProduct(Long productId) {// 1. 先查redisString productRedis = jedis.get(SystemConstants.REDIS_KEY_PREFIX + productId);if (!StringUtil.isBlank(productRedis)) {return gson.fromJson(productRedis, Product.class);}// 2. redis没有,再查mysql数据库Product productMysql = productRepo.findByProductId(productId);if (productMysql != null) {// 3. 数据库有,则更新redis数据jedis.set(SystemConstants.REDIS_KEY_PREFIX + productMysql.getProductId(), gson.toJson(productMysql));}// 4. 返回mysql数据库数据return productMysql;}
小公司并发量不大的情况下,问题不是很大,但是大公司高并发量,会出现大量问题,列举如下:
存在的问题:1. 缓存容量小问题:几百G的海量数据不可能一直都放到redis缓存中,大大降低redis(<10G)作为内存数据库的效率解决方案:设置固定过期时间,比如说一天,虽然一开始redis数据量很大,但是一天之后,会有大量数据失效,达到冷热数据的分离。
jedis.set(SystemConstants.REDIS_KEY_PREFIX + product.getProductId(), gson.toJson(product));
jedis.expire(SystemConstants.REDIS_KEY_PREFIX + product.getProductId(), SystemConstants.REDIS_KEY_EXPIRED_TIME);2. 缓存击穿问题:虽然设置了过期时间,仍然会出现缓存击穿问题, 即单个热点key失效的瞬间,持续的大并发请求就会击破缓存,直接请求到数据库,好像蛮力击穿一样(缓存无数据/数据库有数据)解决方案:设置随机过期时间
jedis.expire(SystemConstants.REDIS_KEY_PREFIX + productId, genRandomExpiredTime(5));
public Integer genRandomExpiredTime(Integer random) {return SystemConstants.REDIS_KEY_EXPIRED_TIME + new Random().nextInt(random) * 60 * 60;
}3. 缓存穿透问题:用户访问的数据既不在缓存当中,也不在数据库中,按道理说数据库都没有这个数据,就不能一直来查数据库了,防止黑客恶意攻击。解决方案一:缓存空值(null)或默认值 + 过期时间在数据库查询不存在时,将其缓存为空值(null)或默认值,缓存失效时间一般设置为5分钟之内,当数据库被写入或更新该key的新数据时,缓存必须同时被刷新,避免数据不一致。@Overridepublic Product getProduct(Long productId) {String redisId = SystemConstants.REDIS_KEY_PREFIX + productId;// 1. 先查redisString productRedis = jedis.get(redisId);if (!StringUtil.isBlank(productRedis)) {// 判断缓存是否是默认值,避免缓存穿透if (productRedis.equals(SystemConstants.REDIS_DEFAULT_CACHE)) {jedis.expire(redisId, genRandomExpiredTime(3));return null;}jedis.expire(redisId, genRandomExpiredTime(5));return gson.fromJson(productRedis, Product.class);}// 2. redis没有,再查mysql数据库Product productMysql = productRepo.findByProductId(productId);if (productMysql != null) {// 3. 数据库有,则更新redis数据jedis.set(redisId, gson.toJson(productMysql));jedis.expire(redisId, genRandomExpiredTime(5));} else {// 缓存空或默认值 + 过期时间,避免缓存穿透jedis.set(redisId, SystemConstants.REDIS_DEFAULT_CACHE);jedis.expire(redisId, genRandomExpiredTime(3));}return productMysql;}4. 突发性热点缓存重建导致数据库系统压力倍增:也就是说某一数据本来是冷数据,存储在数据库中,突然出现大量访问,redis还没缓存该数据,因此需要大量查询数据库并重建缓存,也就是以下代码重复执行,要是只执行一次就好了。if (!StringUtil.isBlank(productRedis)) {// 3. 数据库有,则更新redis数据jedis.set(redisId, gson.toJson(productMysql));jedis.expire(redisId, genRandomExpiredTime(5));}解决方案一:DCL双端检锁机制
但仍然存在以下问题,一方面synchronized锁住的是单个JVM,若是该web项目集群部署,则在每个JVM都需要锁一次,另一方面,假如productId=101是热点数据会被锁住,但是其他数据productId=202也需要排队等待,效率降低。解决方案二:分布式锁setnx
但仍然存在redis缓存和mysql数据库数据不一致问题解决方案三:锁优化-读写锁5. 缓存雪崩:在使用缓存时,通常会对缓存设置过期时间,一方面目的是保持缓存与数据库数据的一致性,另一方面是减少冷缓存占用过多的内存空间。但当缓存中大量热点缓存在某一个时刻同时实效,请求全部转发到数据库,从而导致数据库压力骤增,造成系统崩溃等情况,这就是缓存雪崩。解决方案:1. key均匀失效:   将key的过期时间后面加上一个随机数(比如随机1-5分钟),让key均匀的失效。2. 双key策略:		主key设置过期时间,备key不设置过期时间,当主key失效时,直接返回备key值。3. 构建缓存高可用集群
// 解决方案一:DCL双端检锁机制
@Overridepublic Product getProduct(Long productId) {String redisId = SystemConstants.REDIS_KEY_PREFIX + productId;// 1. 先查redisString productRedis = jedis.get(redisId);if (!StringUtil.isBlank(productRedis)) {// 判断缓存默认值,避免缓存穿透if (productRedis.equals(SystemConstants.REDIS_DEFAULT_CACHE)) {jedis.expire(redisId, genRandomExpiredTime(3));return null;}jedis.expire(redisId, genRandomExpiredTime(5));return gson.fromJson(productRedis, Product.class);}Product productMysql = null;synchronized (this) {// 2. DCL再查redis,因为只要有一次查询数据库操作,redis就已经有缓存数据了productRedis = jedis.get(redisId);if (!StringUtil.isBlank(productRedis)) {if (productRedis.equals(SystemConstants.REDIS_DEFAULT_CACHE)) {jedis.expire(redisId, genRandomExpiredTime(3));return null;}jedis.expire(redisId, genRandomExpiredTime(5));return gson.fromJson(productRedis, Product.class);}// 3. redis还是没有,再查mysql数据库productMysql = productRepo.findByProductId(productId);if (productMysql != null) {// 4. 数据库有,则更新redis数据【可能出现突发性热点缓存重建导致数据库系统压力倍增】jedis.set(redisId, gson.toJson(productMysql));jedis.expire(redisId, genRandomExpiredTime(5));} else {// 缓存空或默认值 + 过期时间,避免缓存穿透jedis.set(redisId, SystemConstants.REDIS_DEFAULT_CACHE);jedis.expire(redisId, genRandomExpiredTime(3));}}return productMysql;}
// 解决方案二:分布式锁setnx<dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.20.0</version></dependency>@Configurationpublic class RedissonConfig {@Beanpublic Redisson redisson() {Config config = new Config();config.useSingleServer().setAddress("redis://localhost:6379").setDatabase(0);return (Redisson) Redisson.create(config);}}// 集群部署:分布式锁public Product getProduct2(Long productId) {String redisId = SystemConstants.REDIS_KEY_PREFIX + productId;// 1. 先查redis缓存Product product = getProductFromRedis(redisId);if (product != null) {return product;}// 分布式锁RLock确保锁住特定的productId,不影响其他productId,解决所有问题RLock lock = redisson.getLock(SystemConstants.LOCK_HOT_CACHE_PREFIX + productId);lock.lock(); // 等价于setnx(SystemConstants.LOCK_HOT_CACHE_PREFIX + productId, value)// 2. DCL再查redis,因为只要有一次查询数据库操作,redis就已经有缓存数据了Product productMysql = null;try {product = getProductFromRedis(redisId);if (product != null) {return product;}// 3. redis还是没有,最后查mysql数据库productMysql = getProductFromMysql(productId);} finally {lock.unlock();}return productMysql;}private Product getProductFromRedis(String redisId) {Product product = null;String productRedis = jedis.get(redisId);if (!StringUtil.isBlank(productRedis)) {if (productRedis.equals(SystemConstants.REDIS_DEFAULT_CACHE)) {// 缓存中存在,却是缓存默认值,也就是数据库没有数据,设置过期时间,避免缓存穿透jedis.expire(redisId, genRandomExpiredTime(3));return new Product(); // 特殊情况}// 缓存中存在,也是正常值jedis.expire(redisId, genRandomExpiredTime(5));product = gson.fromJson(productRedis, Product.class);}return product;}private Product getProductFromMysql(Long productId) {String redisId = SystemConstants.REDIS_KEY_PREFIX + productId;Product productMysql = productRepo.findByProductId(productId);if (productMysql != null) {// 数据库有,则同步更新redis缓存数据【但是可能出现突发性热点缓存重建导致数据库系统压力倍增,也就是这段代码大量执行】jedis.set(redisId, gson.toJson(productMysql));jedis.expire(redisId, genRandomExpiredTime(5));} else {// 数据库没有,则设置默认值缓存 + 过期时间,避免缓存穿透jedis.set(redisId, SystemConstants.REDIS_DEFAULT_CACHE);jedis.expire(redisId, genRandomExpiredTime(3));}return productMysql;}
// 解决方案三:锁优化-读写锁
public Product getProductByReadWriteLock(Long productId) {String redisId = SystemConstants.REDIS_KEY_PREFIX + productId;// 1. 先查redis缓存Product product = getProductFromRedis(redisId);if (product != null) {return product;}// 加写锁ReadWriteLock readWriteLock = redisson.getReadWriteLock(SystemConstants.LOCK_HOT_UPDATE_PREFIX + productId);Lock writeLock = readWriteLock.writeLock();writeLock.lock();// 2. DCL再查redis,因为只要有一次查询数据库操作,redis就已经有缓存数据了Product productMysql;try {product = getProductFromRedis(redisId);if (product != null) {return product;}// 3. 加读锁 读数据库ReadWriteLock readWriteLock2 = redisson.getReadWriteLock(SystemConstants.LOCK_HOT_UPDATE_PREFIX + productId);Lock readLock = readWriteLock2.readLock();readLock.lock();productMysql = getProductFromMysql(productId);readLock.unlock();} finally {writeLock.unlock();}return productMysql;}  

11. redis之缓存击穿、缓存穿透、缓存雪崩

  • 缓存击穿-缓存无数据/数据库有数据

单个热点key失效的瞬间,持续的大并发请求就会击破缓存,直接请求到数据库,好像蛮力击穿一样。这种情况就是缓存击穿(Cache Breakdown)。

在这里插入图片描述

1. 使用互斥锁(Mutex Key)只让一个线程构建缓存,其他线程等待构建缓存执行完毕,重新从缓存中获取数据。
2. 热点数据设置随机过期时间,后台异步更新缓存,适用于不严格要求缓存一致性的场景。
  • 缓存穿透-缓存无数据/数据库无数据

    缓存穿透(cache penetration)是用户访问的数据既不在缓存当中,也不在数据库中。但出于容错的考虑,如果从数据库查询不到数据,则无法写入缓存。这就导致每次请求都会到底层数据库进行查询,缓存也失去了意义。当高并发或有人利用不存在的Key频繁攻击时,数据库的压力骤增,甚至崩溃,这就是缓存穿透问题。

在这里插入图片描述

解决方案:
方案一:缓存空值(null)或默认值 + 过期时间在数据库查询不存在时,将其缓存为空值(null)或默认值,缓存失效时间一般设置为5分钟之内,当数据库被写入或更新该key的新数据时,缓存必须同时被刷新,避免数据不一致。方案二:业务逻辑前置校验在业务请求的入口处进行数据合法性校验,检查请求参数是否合理、是否包含非法值、是否恶意请求等,提前有效阻断非法请求。比如,根据年龄查询时,请求的年龄为-10岁,这显然是不合法的请求参数,直接在参数校验时进行判断返回。方案三:使用布隆过滤器请求白名单写入数据时,使用布隆过滤器进行标记(相当于设置白名单),业务请求发现缓存中无对应数据时,可先通过查询布隆过滤器判断数据是否在白名单内,如果不在白名单内,则直接返回空或失败。方案四:用户黑名单限制当发生异常情况时,实时监控访问的对象和数据,分析用户行为,针对故意请求、爬虫或攻击者,进行特定用户的限制;

在这里插入图片描述

  • 缓存雪崩-缓存无数据/数据库有数据

    在使用缓存时,通常会对缓存设置过期时间,一方面目的是保持缓存与数据库数据的一致性,另一方面是减少冷缓存占用过多的内存空间。但当缓存中大量热点缓存在某一个时刻同时实效,请求全部转发到数据库,从而导致数据库压力骤增,造成系统崩溃等情况,这就是缓存雪崩(Cache Avalanche)。

在这里插入图片描述

解决方案:
1. key均匀失效:   将key的过期时间后面加上一个随机数(比如随机1-5分钟),让key均匀的失效。
2. 双key策略:		主key设置过期时间,备key不设置过期时间,当主key失效时,直接返回备key值。
3. 构建缓存高可用集群(针对缓存服务故障情况)

12. redis实现分布式session

基于redis的分布式session实现,依赖于前台请求中携带的cookie和后台生成的token。大致原理可以分为以下步骤:1,前端请求目标方法,拦截器判断请求头中是否携带cookie。
2,如果请求头中携带cookie,则取出cookie并查询redis中该cookie是否过期。如果没有过期,则放行让该请求去请求目标方法;如果已经过期,重新登陆
3,如果请求头中,没有携带cookie,则跳转到登录方法(同时携带当前请求的链接作为登录后的回调地址)
4,进行登录,登录完毕生成指定的token存入redis中,生成cookie设置到response中。
5,登录成功之后前端通过回调继续请求目标方法。

在这里插入图片描述

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

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

相关文章

MQTT 5.0 报文解析 05:DISCONNECT

欢迎阅读 MQTT 5.0 报文系列 的第五篇文章。在上一篇中&#xff0c;我们已经介绍了 MQTT 5.0 的 PINGREQ 和 PINGRESP 报文。现在&#xff0c;我们将介绍下一个控制报文&#xff1a;DISCONNECT。 在 MQTT 中&#xff0c;客户端和服务端可以在断开网络连接前向对端发送一个 DIS…

手把手教你搭建一个花店小程序商城

如果你是一位花店店主&#xff0c;想要为你的生意搭建一个精美的小程序商城&#xff0c;以下是你将遵循的五个步骤。 步骤1&#xff1a;登录乔拓云平台进入后台 首先&#xff0c;你需要登录乔拓云平台的后台管理页面。你可以在电脑或移动设备上的浏览器中输入乔拓云的官方网站…

2024.5.26 机器学习周报

目录 引言 Abstract 文献阅读 1、题目 2、引言 3、创新点 4、Motivation 5、naive Lite-HRNet 6、Lite-HRNet 7、实验 深度学习 解读SAM(Segment Anything Model) 1、SAM Task 2、SAM Model 2.1、Patch Embedding 2.2、Positiona Embedding 2.3、Transformer …

移动端适配:vw适配方案

vw (Viewport Width) 是一种长度单位&#xff0c;代表视口宽度的百分比。1vw 等于视口宽度的1%。在网页设计和前端开发中&#xff0c;vw 单位常用于实现响应式设计和屏幕适配&#xff0c;尤其是针对不同尺寸和分辨率的移动设备。 为什么使用vw适配&#xff1f; 响应式: 使用v…

互联网医院开发:引领智慧医疗新时代

随着科技的迅猛发展和互联网的普及&#xff0c;传统医疗模式正在迎来一场深刻的变革。互联网医院的崛起&#xff0c;打破了时间和空间的限制&#xff0c;为患者和医疗机构带来了更加便捷、高效、安全的医疗服务体验。本文将从技术角度深入探讨互联网医院的开发&#xff0c;包括…

【openpcdet中yaml文件的DATA_AUGMENTOR学习】

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、代码二、详细解释DISABLE_AUG_LISTAUG_CONFIG_LIST1. gt_sampling2. random_world_flip3. random_world_rotation4. random_world_scaling 总结 前言 提示…

多线程(八)

一、wait和notify 等待 通知 机制 和join的用途类似,多个线程之间随机调度,引入 wait notify 就是为了能够从应用层面上,干预到多个不同线程代码的执行顺序.( 这里说的干预,不是影响系统的线程调度策略 内核里的线程调度,仍然是无序的. 相当于是在应用程序…

Pod容器资源限制和探针

目录 一、资源限制 1.Pod和容器的资源请求和限制 2.CPU 资源单位 案例一 案例二 二、健康检查&#xff0c;又称为探针&#xff08;Probe&#xff09; 1.探针的三种规则 2.Probe支持三种检查方法 3.探测获得的三种结果 案例一&#xff1a;exec 案例二&#xff1a;htt…

OneMO同行 心级服务:中移物联OneMO模组助力客户终端寒冷环境下的稳定运行

中移物联OneMO模组以客户为中心&#xff0c;基于中国移动心级服务要求&#xff0c;开展“OneMO同行 心级服务 标定一流”高标服务主题活动&#xff0c;升级“服务内容““服务方式”和“服务意识”&#xff0c;为行业客户提供全新的服务体验。 近日&#xff0c;某车载监控设备…

Hive语法学习总结

Hive SQL语法学习总结 hive参数库操作1.创建库2.具体案例3.库的其他操作 表和库的路径演示表的操作创建表插入数据 hive参数 一 hive常用交互命令hive -e sql语句hive -f sql文件 //文件中是sql语句二 参数的设置方式一&#xff1a;在客户端中设置参数(当次有效)set 参数名参…

ACM实训第十七天

Is It A Tree? 问题 考试时应该做不出来&#xff0c;果断放弃 树是一种众所周知的数据结构&#xff0c;它要么是空的(null, void, nothing)&#xff0c;要么是一个或的集合满足以下属性的节点之间有向边连接的节点较多。 •只有一个节点&#xff0c;称为根节点&#xff0c;它…

【Crypto】摩丝

文章目录 一、摩斯解题感悟 一、摩斯 很明显莫尔斯密码 iloveyou还挺浪漫 小小flag&#xff0c;拿下 解题感悟 莫尔斯密码这种题还是比较明显的

【董晓算法】竞赛常用知识之图论3(最近公共祖先)

前言&#xff1a; 本系列是学习了董晓老师所讲的知识点做的笔记 董晓算法的个人空间-董晓算法个人主页-哔哩哔哩视频 (bilibili.com) 动态规划系列&#xff08;还没学完&#xff09; 【董晓算法】动态规划之线性DP问题-CSDN博客 【董晓算法】动态规划之背包DP问题&#xff…

智能锁千千万,谁是你的NO.1,亲身实测凯迪仕传奇大师K70旗舰新品

智能锁千千万&#xff0c;谁是你的NO.1。欢迎来到智哪儿评测室&#xff0c;这次我们为大家带来了凯迪仕传奇大师K70系列的一款重磅新品。 在科技的浪潮中&#xff0c;家居安全领域正经历着前所未有的变革。智能锁越来越成为家的安全守护神&#xff0c;以及智能生活的得力助手。…

Android 11 Framework实时监听Activity堆栈变化

核心类 Framework中有一个类SystemActivityMonitoringService专门用于监控Activity堆栈变化&#xff0c;属于隐藏Api&#xff0c;应用侧无法调用。此类位于 packages/services/Car/service/src/com/android/car/SystemActivityMonitoringService.java 方法 void registerTa…

Mysql信息脱敏

类似微信姓名脱敏&#xff1a; SELECT CONCAT( REPEAT(*, CHAR_LENGTH(real_name) -1 ), RIGHT(real_name, 1) ) name from user_info电话号脱敏&#xff1a; SELECT CONCAT(LEFT(mobile_phone, 3), REPEAT(*, 4 ), RIGHT(mobile_phone, 4) ) phone from user_info

大数据Hive中的UDF:自定义数据处理的利器(下)

在上一篇文章中&#xff0c;我们对第一种用户定义函数&#xff08;UDF&#xff09;进行了基础介绍。接下来&#xff0c;本文将带您深入了解剩余的两种UDF函数类型。 文章目录 1. UDAF1.1 简单UDAF1.2 通用UDAF 2. UDTF3. 总结 1. UDAF 1.1 简单UDAF 第一种方式是 Simple(简单…

每日一题《leetcode--382.链表随机结点》

https://leetcode.cn/problems/linked-list-random-node/ 这道题我们首先看到题目中的要求&#xff1a;在单链表中随机选取一个链表中的结点&#xff0c;要使每个结点被选取的概率是一样的。 当我们看到随机这两个字时&#xff0c;应该就会想起rand()这个函数。接着我们把使用这…

[晕事]今天做了件晕事35 VM发送给gateway太多ARP,导致攻击检查?

最近遇到一个问题&#xff0c;说网关学不到新起来VM的mac地址&#xff0c;通过tshark抓包发现&#xff0c;VM已经发出去GARP了。而且连续发送了24个GARP。 就认为是网关的问题&#xff0c;为什么没网关没有学到&#xff1f;就让测试同事开网络设备的ticket。 后来听同事说&…

自己搭建内网穿透

本文介绍使用最新版frp搭建内网穿透&#xff0c;最新版本的frp在配置上与之前有很大不同&#xff0c;需要使用.toml文件进行配置。其中主要问题出现在toml文件内部。 一、云服务器配置 下载frp sudo apt update sudo apt install wget wget https://github.com/fatedier/frp…