合肥企业自助建站,做网站要多少费用,广告设计培训机构,百度最新版app下载安装前言#xff1a;此篇文章系本人学习过程中记录下来的笔记#xff0c;里面难免会有不少欠缺的地方#xff0c;诚心期待大家多多给予指教。 基础篇#xff1a;
Redis#xff08;一#xff09;Redis#xff08;二#xff09;Redis#xff08;三#xff09;Redis#x… 前言此篇文章系本人学习过程中记录下来的笔记里面难免会有不少欠缺的地方诚心期待大家多多给予指教。 基础篇
Redis一Redis二Redis三Redis四Redis五Redis六Redis七Redis八 进阶篇
Redis九Redis十Redis十一Redis十二 接上期内容上期完成了相关案例的学习。下面学习缓存穿透、预热、雪崩、击穿话不多说直接发车。 一、缓存预热
一、定义
缓存预热是一种在系统启动阶段或者特定时间点将一些经常访问或者关键的数据提前加载到缓存中的操作以减少对数据源如数据库的访问次数从而提高系统的响应速度和性能。避免在用户首次请求时才去加载数据而导致的性能延迟。 二、功能
减少首次请求延迟当用户首次访问某些数据时如果没有进行缓存预热系统需要从数据库等数据源中查询数据这个过程可能会比较耗时。减轻数据库压力在系统运行初期如果大量用户同时发起请求这些请求都直接访问数据库会给数据库带来巨大的压力甚至可能导致数据库性能下降或者崩溃。缓存预热可以将部分数据提前加载到缓存中后续的请求优先从缓存中获取数据从而减少了对数据库的访问频率降低了数据库的负载提高系统性能和稳定性由于缓存的读写速度通常比数据库等数据源快很多缓存预热可以让系统在处理请求时更快地获取数据从而提高系统的整体性能。同时减少了对数据库的依赖降低了因数据库故障或性能问题导致系统不可用的风险增强了系统的稳定性。 三、常用方案 硬编码不大推荐在代码中直接明确地指定需要加载到缓存的数据和逻辑不通过外部配置或动态计算来改变。在系统启动时按照预先编写好的代码逻辑将数据加载到缓存中。PostConstruct注解 Java 中的一个注解用于标记一个方法该方法会在依赖注入完成之后、对象正式投入使用之前被自动调用。定时器任务通过定时任务框架如 Spring 的Scheduled注解、Quartz 等按照预设的时间间隔如每天凌晨、每小时等自动执行缓存预热操作。数据脚本使用脚本语言如 Python、Shell 等编写脚本通过脚本连接到缓存系统将数据加载到缓存中。 二、缓存雪崩
一、名词解释
缓存雪崩是指在某一时刻缓存中大量的键在同一时间点或者在极短的时间内集中过期失效或者缓存服务器发生故障导致缓存服务不可用此时大量原本可以从缓存中获取数据的请求都直接涌向了数据库等后端数据源给数据库带来巨大的压力甚至可能导致数据库不堪重负而崩溃进而使整个系统出现性能急剧下降、服务不可用等严重问题。 二、发生场景
1、硬件方面
缓存服务器的硬件设备如硬盘、内存、网卡等出现故障可能会导致缓存服务无法正常运行从而发生缓存雪崩现象。 2、业务方面
大量的业务key同时过期比如在进行缓存预热时为大量缓存数据设置了相同的过期时间当这个过期时间到达时这些缓存数据会同时失效从而引起缓存雪崩现象。 三、预防与解决措施
硬件方面无法把控主要从业务方面来解决。
业务方面 避免大量缓存键同时过期①、设置随机时间在设置缓存键的过期时间时为每个键的过期时间添加一个随机的偏移量避免它们集中在同一时刻过期。②、设置key用不过期。 redis集群实现服务高可用使用缓存服务器的集群模式集群模式可以将数据分散存储在多个节点上当某个节点出现故障时其他节点仍然可以正常提供服务保证缓存服务的可用性。 多缓存结合采用多级缓存架构例如同时使用本地缓存(ehcache)redis缓存。 服务降级在应用层对请求进行限流当请求量超过一定阈值时直接拒绝部分请求避免过多的请求直接访问数据库。 三、缓存穿透
一、名词解释
缓存穿透是指客户端请求的数据在缓存中不存在同时在数据库中也不存在这样每次该请求都会穿透缓存直接访问数据库。如果有大量这样的无效请求持续涌入会对数据库造成极大的压力甚至可能导致数据库不堪重负而崩溃。例如黑客可能会故意发起大量不存在的键的请求以消耗数据库资源。 二、发生场景
黑客恶意攻击攻击者可能会利用系统的漏洞构造大量不存在的请求如不存在的用户 ID、商品 ID 等向系统发起请求。由于这些请求对应的数据在缓存和数据库中都不存在会导致大量请求直接穿透缓存访问数据库从而影响系统的正常运行。业务数据异常在业务系统中可能会出现数据不一致或者数据删除不及时的情况。错误的用户输入如果系统没有对用户输入进行严格的验证用户可能会输入错误的查询条件如输入一个不存在的订单号、手机号码等。这些无效请求会直接穿透缓存访问数据库。 三、预防与解决措施
1、方案一
缓存空值或默认值当查询的数据在数据库中不存在时在缓存中存储一个空值或者默认值并设置一个较短的过期时间。这样下次相同的请求就可以直接从缓存中获取空值或默认值而不会再穿透到数据库。 2、方案二
使用布隆过滤器①、自研布隆过滤器。②、使用Google Guava 库实现布隆过滤器。 四、案例演示
自研简略版布隆过滤器在上一篇已经学习过了下面将学习Google Guava实现方式。Guava源码地址GitHub - google/guava: Google core libraries for Java
1、需求说明
模拟使用Guava布隆过滤器拦截掉非法数字对于合法的数字放行。 2、导入依赖
!-- https://mvnrepository.com/artifact/com.google.guava/guava --
dependencygroupIdcom.google.guava/groupIdartifactIdguava/artifactIdversion32.1.2-jre/version
/dependency3、编码实现
public class GuavaBloomFilterDemo {public static void main(String[] args) {//误判率,它越小误判的个数也就越少(思考是不是可以设置的无限小没有误判岂不更好)//fpp the desired false positive probability 0.0 fpp 1.0,误判率越低消耗的资源越多哈希函数也用的越多误判率也就越低// 默认值 0.03BloomFilterInteger bloomFilter BloomFilter.create(Funnels.integerFunnel(), 1000000, 0.03);//1 先往布隆过滤器里面插入100万的样本数据for (int i 1; i 1000000; i) {bloomFilter.put(i);}ListInteger list1 Arrays.asList(120000000, 111111, 10, 222222222, 42575, 123457);list1.forEach(e - {boolean result bloomFilter.mightContain(e);if (result) {// TODO 查redis → 查数据库 → .....System.out.println(存在放行 e);} else {// 直接返回结果System.out.println(不存在拦截 e);}});//故意取10万个不在过滤器里的值看看有多少个会被认为在过滤器里ListInteger list new ArrayList(1000000);for (int i 1000000 1; i 1100000; i) {if (bloomFilter.mightContain(i)) {list.add(i);}}System.out.println(误判的总数量:{} list.size());}
} 四、缓存击穿
一、名词解释
缓存击穿是指在高并发的场景下一个非常热点的 key 在缓存中过期失效的瞬间大量针对该 key 的请求同时涌入。由于此时缓存中没有该 key 对应的数据这些请求就会全部转向数据库去查询。数据库在短时间内需要处理大量的查询请求从而承受巨大的压力可能会导致数据库性能急剧下降甚至出现崩溃的情况进而影响整个系统的正常运行。 二、发生场景
热点数据过期在电商系统中一款热门商品的信息会被大量用户频繁访问为了减轻数据库压力会将该商品信息缓存起来并设置过期时间。当这个过期时间到达缓存中的数据失效而此时恰好有大量用户同时发起对该商品信息的请求就会出现缓存击穿的情况。流量突发一些突发的热点事件会导致瞬间产生大量的请求。比如某明星突然发布一条微博引发大量粉丝同时访问其个人主页而该主页信息在缓存中过期大量请求就会直接冲向数据库。数据预热不正确比如新闻网站在每天早上进行缓存预热但遗漏了当天的一条重大热点新闻当用户大量访问该新闻时就会出现问题。
三、预防与解决措施
1、方案一
使用双检加锁策略。前面已经学习过了不在阐述。
public String get(String key) {String value redis.get(key);// 查询缓存if (value ! null) {//缓存存在直接返回return value;} else {//缓存不存在则对方法加锁//假设请求量很大缓存过期synchronized (this) {value redis.get(key); // 在查一遍redisif (value null) {// 从数据库获取数据value dao.get(key);// 设置过期时间并回写到缓存redis.setex(key, time, value);}return value;}}} 2、方案二
随机退避策略。当发现缓存中热点 key 失效时让各个请求不要立即去访问数据库而是各自随机等待一段不同的时间后再去尝试获取数据。这样可以避免大量请求在同一时刻集中访问数据库将请求的时间分散开减轻数据库在短时间内的压力。
public String randomBackoffMethod(String key) {try {Jedis jedis RedisUtils.getJedis();String data jedis.get(key);if (data null) {try {// 生成随机退避时间 毫秒int backoffTime new Random().nextInt(2000);Thread.sleep(backoffTime);// 再次尝试从缓存获取数据,// 但是在高并发场景下可能线程随机退避时间会一样// 为了避免造成缓存双写不一致问题使用双检锁策略来防止String value jedis.get(key);// 查询缓存if (value ! null) {//缓存存在直接返回return value;} else {//缓存不存在则对方法加锁//假设请求量很大缓存过期synchronized (this) {value jedis.get(key); // 在查一遍redisif (value null) {// 从数据库获取数据value dao.get(key);// 设置过期时间并回写到缓存jedis.setex(key, time, value);}return value;}}} catch (InterruptedException e) {Thread.currentThread().interrupt();}}jedis.close();return data;} catch (Exception e) {e.printStackTrace();}return null;} 3、方案三
差异失效策略。它的核心思想是避免多个热点 Key 在同一时刻同时失效从而防止大量请求在瞬间全部涌向数据库给数据库造成过大压力。
在缓存击穿场景中的实现方式为一个热点key拷贝两份两份缓存过期时间不一样将缓存失效的时间分散开来以此保障系统的稳定性与性能。 /*** 差异失效策略查询*/public String differentialFailureSelectMethod(String key) {// 同一个热点key的前缀String prefixA keyA:;String prefixB keyB:;try {Jedis jedis RedisUtils.getJedis();String data jedis.get(prefixA key);if (data null) {System.out.println(A缓存已经失效);//用户先查询缓存A(上面的代码)如果缓存A查询不到例如更新缓存的时候删除了再查询缓存Bdata jedis.get(prefixB key);if (data null) {System.out.println(B缓存已经失效);//TODO 查数据库 → 回写redis}return data;}System.out.println(查询结果{} data);} catch (Exception ex) {ex.printStackTrace();}return null;}/*** 差异失效策略更新*/public void differentialFailureUpdateMethod(String key) {try {// 以前的某个热点keyString oldHotKeyA oldHotKey: key;String oldHotKeyB oldHotKey: key;// 新热点key前缀String prefixA newKeyA:;String prefixB newKeyB:;//模拟从数据库查数据Jedis jedis RedisUtils.getJedis();Object o dao.get(key);//先更新B缓存jedis.del(oldHotKeyB);jedis.set(prefixB o.getId());jedis.expire(prefixB o.getId(), 2000);//再更新A缓存jedis.del(oldHotKeyA);jedis.set(prefixA o.getId());jedis.expire(prefixA o.getId(), 1000);} catch (Exception e) {e.printStackTrace();}} 五、总结
一图总结四个问题以及解决办法
问题类型核心问题解决方案典型场景缓存预热数据未提前加载启动加载、定时任务、脚本电商大促前的商品信息加载缓存雪崩大量缓存失效随机过期、集群、高可用、限流大量key过期、缓存服务器宕机时缓存穿透无效请求攻击布隆过滤器、缓存空值恶意攻击场景缓存击穿热点 Key 失效双检锁、差异失效、随机退避秒杀活动中的商品信息访问 ps努力到底让持续学习成为贯穿一生的坚守。学习笔记持续更新中。。。。