Redis缓存的作用,大大降低了我们数据库的访问压力,极大提高了我们的网站的性能了服务效率,特别在数据查询这一块,是关系型数据库(mysql , oracle ,SQLserver)远远做不到的
在所线程高并发的条件下,特别是做电商项目的时候出现高并发怎么解决这些问题?
一、缓存穿透
缓存穿透,是指我们本应该让用户去缓存中查询获取的数据,结果却去数据库中查询,造成我们数据库的压力过载。这种情况只可能在多线程高并发的条件下出现,如何解决这种现象发生
二重检测 + 同步代码块 技术
看一个例子,这是我一个p2p项目的片段
//获取历史平均年划收益率
@Override
public Double queryHistoryAverageRate() {
//1. 将redisTemplate模板对象的key 序列化方式修改 为 StringRedisSerializer
//提高代码的可读性
redisTemplate.setKeySerializer(new StringRedisSerializer());
//以下代码在多线程并发的情况下会出现缓存穿透的问题
//需要的数据先从Redis缓存域中查询出来 , 如果没有 先去数据库中查询 将查询出来的结果放入 到Redis缓存域中
//利用双重检测技术加上同步代码块 保证只有第一个数据是从数据库中查询出来的 ,
//第二个以及往后的数据都必须是从Redis缓存域中查询出来的
Double historyAverageRate = (Double) redisTemplate.opsForValue().get(Constants.HISTORY_AVERAGE_RATE);
//判断是否为空
if(historyAverageRate == null){
//设置同步代码块
synchronized (this){
//再次从Redis缓存获取去该值
historyAverageRate = (Double) redisTemplate.opsForValue().get(Constants.HISTORY_AVERAGE_RATE);
//再次去判断
if (historyAverageRate == null ){
System.out.println("从数据库中查询");
//去数据库中查询
historyAverageRate = loanInfoMapper.selectHistoryAverageRate();
//将查询到的结果存入到Redis缓存域中
redisTemplate.opsForValue().set(Constants.HISTORY_AVERAGE_RATE, historyAverageRate, 20, TimeUnit.MINUTES);
}
}
}
return historyAverageRate;
这个例子中为了获取平均年化收益率,我们当然一般练习时,可以直接去数据库中查询出来,那么在实际的项目开发中,这种查询数据量大的数据,访问频繁的数据,我们首先考虑的就是放入缓存中(例如Redis)大大提高我们的查询效率,双重检测+同步代码块技术,能够绝对保证只有第一条数据是从数据库中查询出来的,以后的数据必须保证全部是从缓存中获取。
当然这种情况是适用于并发量中型项目(大概不超过5万左右吧)
二、缓存雪崩
缓存雪崩的情况,是指在多线程高并发的条件下,在同一时期,缓存集体过期失效,基本上只在电商项目中见得到,例如,在即将12点时,我们的平台会迎来一波抢购这时候我们会把访问量大的数据放入到缓存中, 设置缓存时间为1个小时,那么1点的时候,这一批商品的缓存时间集体失效,那么用于发起的请求压力将全部交给我们的数据库来承受,这就造成我们数据库的压力过载问题,这种现象就称为缓存雪崩, 那么如何解决缓存雪崩的情况发生。
对于不同分类商品,设置不同缓存周期
对于同一类产品加随机因子
尽可能的分散开来,避免请求波峰的产生。此外对于冷门商品设置的缓存时间可以短一点,热门的商品设置的缓存时间长一点,都是错开缓存周期。避免数据库的压力过载导致服务器崩溃。
三、缓存击穿
在国内能够导致缓存击穿的电商平台应该是为数不多的。缓存击穿针对一个点,不停地扛着大并发,集中进行访问,在key失效的瞬间,高并发穿透缓存,犹如我们看的生化危机的电影中一群僵尸被门挡在外面,顷刻间如开闸之水一般,当然这种 “爆款”太少了。
解决方式加互斥锁 设置永不过期,使用redis缓存确实可以,可以不设置过期时间。但是很少有数据不设置过期时间,那么我们的redis缓存的数据就太庞大了,完成成了一个静态的,所以我们把过期时间存在value中,发现要过期时通过一个后台的异步线程进行缓存的构建,也称为逻辑过期