redis缓存击穿(redis缓存击穿解决方案)

本篇文章给大家谈谈redis缓存击穿,以及redis缓存击穿解决方案对应的知识点,希望对各位有所帮助,不要忘了收藏本站喔。

本文目录一览:

接口添加redis缓存之后并发还是很低

把redis作为缓存使用已经是司空见惯,但是使用redis后也可能会碰到一系列的问题,尤其是数据量很大的时候,经典的几个问题如下:

(一)缓存和数据陪绝库间数据一致性问题册键

分布式环境下(单机就不用说了)非常容易出现缓存和数据库间的数据一致性问题,针对这一点的话,只能说,如果你的项目对缓存的要求是强一致性的,那么请不要使用缓存。我们只能采取合适的策略来降低缓存和数据库间数据不一致的概率,而无法保证两者间的强一致性。合适的策略包括 合适的缓存更新策略,更新数据库后要及时更新缓存、缓存失败时增加重试机制,例如MQ模式的消息队列。

(二)缓存击穿问题芦姿姿

[img]

综合中间件Redisson实战

布隆过滤器也可以用于解决Redis缓存轮唤卖击穿。

RMap的特性:

Redisson中包含有序集合功能组腊逗件SortedSet,积分排序集合功能组件ScoredSortedSet,字典排序集合功能组件LexSortedSet等。

队列底层核心的执行逻辑是记录"基于发布-订阅"的主题来实现。

在使链档用延迟队列的时候,不要将TTL不同的时间放入到同一个队列,Redisson可以按照TTL从小到大的顺序先后被真正的队列监听、消费。

一次性锁:在高并发的时候,如果当前线程可以获取到分布式锁,则成功获取,如果获取不到,当前线程将永远的被抛弃。

用户重复提交场景:

分布式锁的可重入,当高并发产生多线程时,如果当前线程不能获取到分布式锁,它并不会立即被抛弃,而是等待一定的时间,重新尝试去获取分布式锁。

如果在规定的时间内获取到锁,执行后面的操作,如果不能获取到锁,就会被抛弃。

(分布式中间件技术实战(Java版).钟林森.机械工业出版社)

java灰度现网缓存兼容性问题

java灰谈团度现网缓存兼容性问题 ,java缓存一致性问题及解决方案:使用缓存,肯定会存在一致性问题;

读取缓存步骤一般没含模橘有什么问题,但是一旦涉及到数据更新:数据库和缓存更新,就容 易出现缓存(Redis)和数据库(MySQL)间的数据一致性问题。

一、讨论一致性问题之前,先来看一个更新的操作顺序问题:

先删除缓存,再更新数据库

问题:同时有一个请求 A 进行更新操作,一个请求 B 进行查询操作。可能出现:

(1)请求 A 进行写操作(key = 1 value = 2),先删除缓存 key = 1 value = 1

(2)请求 B 查询发现缓存不存在

(3)请求 B 去数据库查询得到旧值 key = 1 value = 1

(4)请求 B 将旧值写入缓存 key = 1 value = 1

(5)请求 A 将新值写入数据库 key = 1 value = 2

缓存中数据永远都是脏数据

我们比较推荐操作顺序:

先删除缓存,再更新数据库,再删缓存(双删,第二次删可异步延时)

public void write(String key,Object data){

redis.delKey(key);

db.updateData(data);

Thread.sleep(500);

redis.delKey(key);

}

接下来,看一看缓存同步的一些方案,见下图:

1、 数据实时同步更新

更新数据库同时更新缓存,使用缓存工具类和或码陪编码实现。

优点:数据实时同步更新,保持强一致性

缺点:代码耦合,对业务代码有侵入性

2、 数据准实时更新

准一致性,更新数据库后,异步更新缓存,使用观察者模式/发布订阅/MQ 实现;

优点:数据同步有较短延迟 ,与业务解耦

缺点:实现复杂,架构较重

3 、缓存失效机制

弱一致性,基于缓存本身的失效机制

优点:实现简单,无须引入额外逻辑

缺点:有一定延迟,存在缓存击穿/雪崩问题

4、 定时任务更新

最终一致性,采用任务调度框架,按照一定频率更新

优点:不影响正常业务

优点:不保证一致性,依赖定时任务

二、 缓存击穿、缓存雪崩及解决方案

1 、缓存击穿

缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于 并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力

瞬间增大,造成过大压力

2 、缓存雪崩

缓存雪崩是指缓存中数据大批量到过期时间,而查询数据量巨大,引起数据库压 力过大甚至 down 机。和缓存击穿不同的是,缓存击穿指并发查同一条数据,缓存雪崩

是不同数据都过期了,很多数据都查不到从而查数据库。

解决方案:

1)单体服务:此时需要对数据库的查询操作,加锁 ---- lock (因考虑到是对同一个参数数值上 一把锁,此处 synchronized 机制无法使用) 加锁的标准流程代码如下:

/**

* 解决缓存雪崩和击穿方案

*/

@Service("provincesService")

public class ProvincesServiceImpl3 extends ProvincesServiceImpl implements ProvincesService{

private static final Logger logger = LoggerFactory.getLogger(ProvincesServiceImpl3.class);

@Resource

private CacheManager cm;//使用注解缓存

private ConcurrentHashMapString, Lock locks = new ConcurrentHashMap();//线程安全的

private static final String CACHE_NAME = "province";

public Provinces detail(String provinceid) {

// 1.从缓存中取数据

Cache.ValueWrapper valueWrapper = cm.getCache(CACHE_NAME).get(provinceid);

if (valueWrapper != null) {

logger.info("缓存中得到数据");

return (Provinces) (valueWrapper.get());

}

//2.加锁排队,阻塞式锁---100个线程走到这里---同一个sql的取同一把锁

doLock(provinceid);//32个省,最多只有32把锁,1000个线程

try{//第二个线程进来了

// 一次只有一个线程

//双重校验,不加也没关系,无非是多刷几次库

valueWrapper = cm.getCache(CACHE_NAME).get(provinceid);//第二个线程,能从缓存里拿到值?

if (valueWrapper != null) {

logger.info("缓存中得到数据");

return (Provinces) (valueWrapper.get());//第二个线程,这里返回

}

Provinces provinces = super.detail(provinceid);

// 3.从数据库查询的结果不为空,则把数据放入缓存中,方便下次查询

if (null != provinces){

cm.getCache(CACHE_NAME).put(provinceid, provinces);

}

return provinces;

}catch(Exception e){

return null;

}finally{

//4.解锁

releaseLock(provinceid);

}

}

private void releaseLock(String userCode) {

ReentrantLock oldLock = (ReentrantLock) locks.get(userCode);

//查询锁是否存在和查询当前线程是否保持此锁

if(oldLock !=null oldLock.isHeldByCurrentThread()){

oldLock.unlock();

}

}

private void doLock(String lockcode) {//给一个搜索条件,对应一个锁

//provinceid有不同的值,参数多样化

//provinceid相同的,加一个锁,---- 不是同一个key,不能用同一个锁

ReentrantLock newLock = new ReentrantLock();//创建一个锁

Lock oldLock = locks.putIfAbsent(lockcode, newLock);//若已存在,则newLock直接丢弃

if(oldLock == null){

newLock.lock();//首次加锁,成功取锁,执行

}else{

oldLock.lock();//阻塞式等待取锁

}

}

}

2} 集群或微服务场景下:

此场景下的锁换成分布式锁(redis或zk等);同时设置多次取锁功能;

/**

* 解决缓存雪崩和击穿方案

*/

@Service("provincesService")

public class ProvincesServiceImpl5 extends ProvincesServiceImpl implements ProvincesService{

private static final Logger logger = LoggerFactory.getLogger(ProvincesServiceImpl3.class);

@Resource

private CacheManager cm;//使用注解缓存

@Autowired

private RedisTemplateString, Object redisTemplate;

private ConcurrentHashMapString, Lock locks = new ConcurrentHashMap();//线程安全的

private static final String CACHE_NAME = "province";

public Provinces detail(String provinceid) throws Exception{

// 1.从缓存中取数据

Cache.ValueWrapper valueWrapper = cm.getCache(CACHE_NAME).get(provinceid);

if (valueWrapper != null) {

logger.info("缓存中得到数据");

return (Provinces) (valueWrapper.get());

}

//2.加锁排队,阻塞式锁---100个线程走到这里---同一个sql的取同一把锁

//32个省,最多只有32把锁,1000个线程

boolean flag=false;

flag = RedisUtil.setNX(provinceid, 3000);

//如果首次没有取到锁,可以取10次

if(!flag){

for(int i=0;i10;i++){

Thread.sleep(200);

flag = RedisUtil.setNX(provinceid, 3000);//分布式锁

if(flag){

break;

}

}

}

//如果首次没有取到锁,一直取直到取到为止

/* if(!flag){

for (;;){

Thread.sleep(200);

flag = RedisUtil.setNX(provinceid, 3000);//分布式锁

if(flag){

break;

}

}

}*/

try{//第二个线程进来了

// 一次只有一个线程

//双重校验,不加也没关系,无非是多刷几次库

valueWrapper = cm.getCache(CACHE_NAME).get(provinceid);//第二个线程,能从缓存里拿到值?

if (valueWrapper != null) {

logger.info("缓存中得到数据");

return (Provinces) (valueWrapper.get());//第二个线程,这里返回

}

Provinces provinces = super.detail(provinceid);

// 3.从数据库查询的结果不为空,则把数据放入缓存中,方便下次查询

if (null != provinces){

cm.getCache(CACHE_NAME).put(provinceid, provinces);

}

return provinces;

}catch(Exception e){

return null;

}finally{

//4.解锁

RedisUtil.releaseLock(provinceid);

}

}

}

这里加分布式锁解决缓存一致性问题,也解决缓存击穿的问题;分布式锁参考:分布式锁使用及原理。

今天缓存一致性问题到此结束,下篇我们使用布隆过滤器解决缓存穿透问题,敬请期待。

缓存击穿、穿透、雪崩及Redis分布式锁

分布式锁: setnx ,redisson 并发问题

幂等问题: 落表状态,Redis

缓存击穿: 指缓存中无,db中有

原因: 一个key高并发恰好失效导致大量请求到db

方案: 加锁,自旋锁,或一个线程查芹乎db,一个线程监控(直接用Redisson分布式锁)

缓存穿透:指缓存和db中均无

原因: 一般是恶意请求

方案: 加布隆过滤,或查db无时,也设置缓存,value为某些特殊表示或"null"

雪崩:指缓存同时大量失效

原因: 大量的key同时失效,db压力加大

方案: 设置失效时间是增加随机数

问题方案文献:

(图例洞首简分析)

Redis分布式锁:

事务未执行完锁已到期释放问题:使用Redissoin解决续租问题,内部已解决

分布式锁文献:

(setnx + expire同时操作)

====================================

Redis原理与应纳裤用

redis缓存机制一般会影响软件的哪些功能?

Redis缓存机制主要作用在于提高数据访问速度、减轻数据库压力、提高系统性能。但是,使用Redis缓存机制可能会影响软件的以下功能:

数据一致性:由于Redis缓存中的数据与数据库中的数据可能存在不一致的情况,这会导致用户在查询数据时看到不一致的结果。

数据过期:缓存数据有祥携过期时间,如果缓存数据过期,需要重新从数据库中获取,这可能会影响查询速度。

数蔽册据持久化:Redis提供了RDB和AOF两种持久化策略,但在某些情况下,如意外宕机等,可能会导致缓存数据的丢失。

内存限制:Redis是基于内存的存储系统,当缓存数据量过大时,可能会消耗大量内存资源,影响软件其他功能的性能。

缓存穿透、缓存击穿和缓存雪崩:这些现象可能导致缓存系统承受较大压力,进而影响整个软件的性能和稳定性。

分布式环境:在分布式环境下,需要考虑缓存数据的同步和一致性问题,否则可能会导致软件功能异常。

缓存维护:需要定期对缓存进行维护,如清除无用的缓存数据,避免缓存数据过多影响系统性能。

在使用Redis缓存机制时,需要充分考虑这些可能影响软件功能的因素,谨并伏并采取相应的措施进行优化。

12 个问题搞懂 Redis

都说学习需要带着问题,带着思考进行学习,下面就以问题的形式来学习下 Redis 。

1、什么是 Redis ?

2、都说 Redis 是单线程模型,到底是什么意思?

3、为什么在数据读写处理上不使用多线程?

4、为什么使用单线程,速度却很快?

5、单线程处理的瓶颈是什么?

6、Redis 6.0 调整为多线程的原因?歼慎

7、在 Redis 中怎样做持久化?

8、常说的缓存雪崩、击穿、穿透是什么?

9、怎样解决雪崩、击穿、穿透带来的虚改派问题?

10、怎样设计缓存的淘汰机制?

11、怎样保证缓存差贺和数据库的数据一致?

12、Redis 有什么使用规范?

Redis 的知识远不止如此,本文总结了一些我认为比较重要的一些点,希望对您有所帮助!

关于redis缓存击穿和redis缓存击穿解决方案的介绍到此就结束了,不知道你从中找到你需要的信息了吗 ?如果你还想了解更多这方面的信息,记得收藏关注本站。

标签列表