数据库
首页 > 数据库> > Redis缓存设计遇到的问题及解决方案

Redis缓存设计遇到的问题及解决方案

作者:互联网

文章目录


前言

今天和大家分享Redis作为缓存使用时常见的问题以及解决方案。像缓存穿透、缓存失效(击穿)以及缓存雪崩等问题的解决。


一、缓存穿透?

Redis中间件在高并发的系统设计中基本上是比不可少的元素。其中在作为缓存使用时有一些问题需要我们处理好。例如缓存穿透,先解释一下什么是缓存穿透?
缓存穿透是指查询一个根本不存在的数据导致缓存层和持久层都不命中。缓存失去了保护持久层的意义。一般我们都会这样做,数据请求进来,我们会先去缓存中查询,当缓存中没有的时候在去持久层查询,持久层查询以后将查询到的数据写到缓存中,如果持久层没有查询到,就不会写入到缓存中。而正是这种压根不存在的数据如果请求量大的时候最终都会打到持久层,从而导致持久层挂掉。整个系统崩溃。那怎么解决?
解决方案:


二、缓存失效(击穿)

缓存失效或者缓存击穿是由于大批量的数据同时失效导致一瞬间大量的请求都打到持久层,导致持久层资源耗尽最终系统崩溃。那导致大批量数据同时失效是因为我们在缓存中存入数据的时候设置的过期时间都是一样的。
解决方案:对大批量的缓存数据的过期时间我们给他设置一个时间范围内。这样,一瞬间过期的几率就很小。
代码如下:

	@Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private RedisCacheService cacheService;
    
	if (key == null || key.equals("")){
            return "key参数为空";
        }
        String cacheData = stringRedisTemplate.opsForValue().get(key);
        if(cacheData != null){
            System.out.println("从缓存中返回");
            return cacheData;
        }else {
            //缓存为空,去查询数据库,这里就模拟一下
            String s = cacheService.searchDB(key);
            stringRedisTemplate.opsForValue().set(key,s);
            if(s.equals("null")){
            	//设置一段时间范围没的时间
                int time = new Random().nextInt(100)+100;
                stringRedisTemplate.expire(key,time, TimeUnit.SECONDS);
            }
            return s;
        }

三、缓存雪崩

缓存雪崩是指缓存层支撑不住或者宕机了,所有的流量都疯狂的涌向了持久层。缓存层承载着大量的请求,能够有效的保护持久层,但是如果缓存层设计的不好或者某些原因导致宕机,从而最终就会出现雪崩效应。导致系统无法正常运行。
解决方案:

  1. 采用高可用的缓存架构,例如哨兵架构、集群架构等
  2. 依赖隔离组件为后端限流熔断并降级。比如使用Sentinel或Hystrix限流降级组件。
  3. 提前预估演练。

四、热点缓存Key的重建优化

“缓存+过期时间”的策略既可以加速数据读写, 又保证数据的定期更新, 这种模式基本能够满足绝大部分需求。 但是有两个问题如果同时出现, 可能就会对应用造成致命的危害:

在缓存失效的瞬间, 有大量线程来重建缓存, 造成后端负载加大, 甚至可能会让应用崩溃。要解决这个问题主要就是要避免大量线程同时重建缓存。我们可以利用互斥锁来解决,此方法只允许一个线程重建缓存, 其他线程等待重建缓存的线程执行完, 重新从缓存获取数据即可。

五、缓存与数据库的双写不一致

在超高并发的情况下,同时操作数据库和缓存可能会存在数据的不一致性问题。

1、双写不一致

通过一张图说明什么是双写不一致
在这里插入图片描述
通过上图可以看出,有两个线程T1和T2,T1先开始写数据库,过了一会,T2也开始写数据库,T2写完数据库更新最新的数据到缓存,这时,T1线程完成了对数据库的写操作。也把数据更新到缓存。很明显,数据库最新的数据应该是T2,但是缓存中却是T1。这就是双写不一致。

2、读写并发不一致

在这里插入图片描述
读写并发不一致如上图。在双写不一致的时候,网上有人爆出说什么“延迟双删”,顾名思义就是延迟一段时间在删除,那问题来了,延迟多少时间删除合适。所以没有解决根本问题。

3、 解决方案:

1、对于并发几率很小的数据(如个人维度的订单数据、用户数据等),这种几乎不用考虑这个问题,很少会发生缓存不一致,可以给缓存数据加上过期时间,每隔一段时间触发读的主动更新即可。
2、就算并发很高,如果业务上能容忍短时间的缓存数据不一致(如商品名称,商品分类菜单等),缓存加上过期时间依然可以解决大部分业务对于缓存的要求。
3、如果不能容忍缓存数据不一致,可以通过加读写锁保证并发读写或写写的时候按顺序排好队,读读的时候相当于无锁。
4、也可以用阿里开源的canal通过监听数据库的binlog日志及时的去修改缓存,但是引入了新的中间件,增加了系统的复杂度。
在这里插入图片描述

六、总结

以上我们针对的都是读多写少的情况加入缓存提高性能,如果写多读多的情况又不能容忍缓存数据不一致,那就没必要加缓存了,可以直接操作数据库。放入缓存的数据应该是对实时性、一致性要求不是很高的数据。切记不要为了用缓存,同时又要保证绝对的一致性做大量的过度设计和控制,增加系统复杂性。

标签:缓存,解决方案,数据库,Redis,布隆,key,过滤器,数据
来源: https://blog.csdn.net/qq_32403043/article/details/122253010