博主系统内redis的使用伪代码如下:

Object o = redis.get(key);  //1

    if(o != null){
        return (Student)o;  //2
    }

    Student value = read db;  //3

    return value;  //4

这段代码也不知道最早谁开始使用的,反正就是这么流传下来了,其实这也是正常的逻辑,先从缓存内读取,如果有,直接返回。如果没有,从DB读取。

今天是来研究缓存方面的问题,所以我们先来看看缓存穿透的问题。

缓存穿透:指查询一个根本不存在的数据,缓存层和持久层都不会命中,但是出于容错的考虑,如果持久层都查不到的话就不会往缓存层去更新数据。

后果:导致了不存在的数据每一次都去持久层查询数据,失去了缓存保护后端的意义。

分析原因:1.可能是自身代码的错误导致了很多空命中,需要去读持久层的数据。

               2.恶意攻击导致的空命中,一下子全跑去持久层,导致后台宕机。

解决方案:

1.缓存空对象。

缓存空对象是一个有效的方法,当步骤3获取不到数据时,为当前key缓存一个空对象,之后的访问就从缓存中读取,保护了后端数据。

带来的问题:

1.1使用更多的内存空间(如果是攻击的话,影响更严重),可行解决办法:针对此类数据设置一个较短的失效时间。

1.2数据不一致问题,当首次查询不存在时缓存了空对象,但是紧接着,持久层添加了这个数据,就会导致持久层和缓存层数据不一致的现象,因此这边需要注意缓存内容的更新。

2.布隆过滤器。

参考文章:

布隆过滤器简介

布隆过滤器

算法的话比较复杂,也不是我关注的对象,不过稍微记录下几个术语吧。

False Positive:FP(集合内没有该元素,查找结果有该元素),就是误报。

False Negative:FN(集合内有该元素,查找结果没有该元素),就是漏报。

适用场景:数据实时性不高,数据命中要求不高,相对固定的场景。

两种方案对比

redis 数据热点 数据倾斜_数据


热点key的重建优化

一般采用缓存+过期时间的策略可以很好加速数据读写,但是一旦有两个问题同时出现的话,可能造成致命缺陷。

1.当前key是一个热点key,并发量特别大。

2.重建缓存的过程比较缓慢。

因此在缓存失效的时候会出现大量线程来重建缓存,可能造成后端负载过大导致崩溃。

解决问题需要注意的点:

减少缓存重建次数,数据尽可能一致,注意其他潜在风险。

解决方式:

1. 互斥锁

只允许一个线程重建缓存,其他线程等待重建的线程执行完再从缓存读取数据。

redis 数据热点 数据倾斜_持久层_02

2.永远不过期。

2.1不设置key的过期时间。

2.2设置value的逻辑过期时间。

redis 数据热点 数据倾斜_redis 数据热点 数据倾斜_03

redis 数据热点 数据倾斜_数据_04


重点回顾:

redis 数据热点 数据倾斜_持久层_05

参考文章:

redis开发与运维