缓存雪崩和缓存穿透
2021/5/22 18:30:02
本文主要是介绍缓存雪崩和缓存穿透,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
缓存雪崩
发生:
redis主机挂了,Redis 全盘崩溃
比如缓存中有大量数据同时过期
解决:
redis缓存集群实现高可用,主从+哨兵
Redis Cluster
ehcache本地缓存 + Hystrix或者阿里sentinel限流&降级
开启Redis持久化机制aof/rdb,尽快恢复缓存集群
缓存穿透:
请求去查询一条记录,先redis后mysql发现都查询不到该条记录,
但是请求每次都会打到数据库上面去,导致后台数据库压力暴增,
这种现象我们称为缓存穿透,这个redis变成了一个摆设。。。。。。
简单说就是本来无一物,既不在Redis缓存中,也不在数据库中
危害:
第一次来查询后,一般我们有回写redis机制
第二次来查的时候redis就有了,偶尔出现穿透现象一般情况无关紧要
解决:
方案1:空对象缓存或者缺省值
黑客或者恶意攻击:
黑客会对你的系统进行攻击,拿一个不存在的id 去查询数据,会产生大量的请求到数据库去查询。可能会导致你的数据库由于压力过大而宕掉
id相同打你系统:
第一次打到mysql,空对象缓存后第二次就返回null了,避免mysql被攻击,不用再到数据库中去走一圈了
id不同打你系统:
由于存在空对象缓存和缓存回写(看自己业务不限死),redis中的无关紧要的key也会越写越多**(记得设置redis过期时间)**
方案2:Google布隆过滤器Guava解决缓存穿透
Guava 中布隆过滤器的实现算是比较权威的,所以实际项目中我们不需要手动实现一个布隆过滤器
Guava’s BloomFilter 源码剖析:
https://github.com/google/guava/blob/master/guava/src/com/google/common/hash/BloomFilter.java
<!--guava Google 开源的 Guava 中自带的布隆过滤器--> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>23.0</version> </dependency>
helloworld案例:
@Test public void bloomFilter() { // 创建布隆过滤器对象 BloomFilter<Integer> filter = BloomFilter.create(Funnels.integerFunnel(), 100); // 判断指定元素是否存在 System.out.println(filter.mightContain(1)); System.out.println(filter.mightContain(2)); // 将元素添加进布隆过滤器 filter.put(1); filter.put(2); System.out.println(filter.mightContain(1)); System.out.println(filter.mightContain(2)); }
新建测试类类BloomfilterDemo
取样本100W数据,查查不在100W范围内的其它10W数据是否存在
import com.google.common.hash.BloomFilter; import com.google.common.hash.Funnels; import java.util.ArrayList; import java.util.List; public class BloomfilterDemo { public static final int _1W = 10000; //布隆过滤器里预计要插入多少数据 public static int size = 100 * _1W; //误判率,它越小误判的个数也就越少(思考,是不是可以设置的无限小,没有误判岂不更好) public static double fpp = 0.03; // 构建布隆过滤器 private static BloomFilter<Integer> bloomFilter = BloomFilter.create(Funnels.integerFunnel(), size,fpp); public static void main(String[] args) { //1 先往布隆过滤器里面插入100万的样本数据 for (int i = 0; i < size; i++) { bloomFilter.put(i); } //故意取10万个不在过滤器里的值,看看有多少个会被认为在过滤器里 List<Integer> list = new ArrayList<>(10 * _1W); for (int i = size+1; i < size + 100000; i++) { if (bloomFilter.mightContain(i)) { System.out.println(i+"\t"+"被误判了."); list.add(i); } } System.out.println("误判的数量:" + list.size()); } }
上一步结论
现在总共有10万数据是不存在的,误判了3033次,
原始样本:100W
不存在数据:101W—110W
debug源码分析下,看看hash函数
布隆过滤器说明:
方案3:Redis布隆过滤器解决缓存穿透
Guava缺点说明
Guava 提供的布隆过滤器的实现还是很不错的 (想要详细了解的可以看一下它的源码实现),但是它有一个重大的缺陷就是只能单机使用 ,而现在互联网一般都是分布式的场景。
为了解决这个问题,我们就需要用到 Redis 中的布隆过滤器了
案例:白名单过滤器:
白名单架构说明:
误判问题,但是概率小可以接受,不能从布隆过滤器删除
全部合法的key都需要放入过滤器+redis里面,不然数据就是返回null
code:
import org.redisson.Redisson; import org.redisson.RedissonBloomFilter; import org.redisson.api.RBloomFilter; import org.redisson.api.RBucket; import org.redisson.api.RedissonClient; import org.redisson.client.codec.StringCodec; import org.redisson.config.Config; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import java.util.concurrent.TimeUnit; public class RedissonBloomFilterDemo2 { public static final int _1W = 10000; //布隆过滤器里预计要插入多少数据 public static int size = 100 * _1W; //误判率,它越小误判的个数也就越少 public static double fpp = 0.03; static RedissonClient redissonClient = null; static RBloomFilter rBloomFilter = null; static { Config config = new Config(); config.useSingleServer().setAddress("redis://192.168.111.147:6379").setDatabase(0); //构造redisson redissonClient = Redisson.create(config); //通过redisson构造rBloomFilter rBloomFilter = redissonClient.getBloomFilter("phoneListBloomFilter",new StringCodec()); rBloomFilter.tryInit(size,fpp); // 1测试 布隆过滤器有+redis有 rBloomFilter.add("10086"); redissonClient.getBucket("10086",new StringCodec()).set("chinamobile10086"); // 2测试 布隆过滤器有+redis无 //rBloomFilter.add("10087"); //3 测试 ,都没有 } public static void main(String[] args) { String phoneListById = getPhoneListById("10087"); System.out.println("------查询出来的结果: "+phoneListById); //暂停几秒钟线程 try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } redissonClient.shutdown(); } private static String getPhoneListById(String IDNumber) { String result = null; if (IDNumber == null) { return null; } //1 先去布隆过滤器里面查询 if (rBloomFilter.contains(IDNumber)) { //2 布隆过滤器里有,再去redis里面查询 RBucket<String> rBucket = redissonClient.getBucket(IDNumber, new StringCodec()); result = rBucket.get(); if(result != null) { return "i come from redis: "+result; }else{ result = getPhoneListByMySQL(IDNumber); if (result == null) { return null; } // 重新将数据更新回redis redissonClient.getBucket(IDNumber,new StringCodec()).set(result); } return "i come from mysql: "+result; } return result; } private static String getPhoneListByMySQL(String IDNumber) { return "chinamobile"+IDNumber; } }
重要总结:
黑名单使用
在centos7下布隆过滤器2种安装方式:
采用docker安装RedisBloom,推荐:
Redis 在 4.0 之后有了插件功能(Module),可以使用外部的扩展功能,
可以使用 RedisBloom 作为 Redis 布隆过滤器插件。
docker run -p 6379:6379 --name=redis6379bloom -d redislabs/rebloom
docker exec -it redis6379bloom /bin/bash redis-cli
布隆过滤器常用操作命令:
bf.reserve filter 0.01 100
bf.add filter v11
bf.exists filter v11
bf.exists filter v12
bf.reserve key error_rate的值 initial_size 的值
默认的error_rate是 0.01,
默认的initial_size是 100。
bf.add key 值
bf.exists key 值
bf.madd 一次添加多个元素
bf.mexists 一次查询多个元素是否存在
编译安装:
# 下载 编译 安装Rebloom插件 wget https://github.com/RedisLabsModules/rebloom/archive/v2.2.2.tar.gz # 解压 tar -zxvf v2.2.2.tar.gz cd RedisBloom-2.2.2 # 若是第一次使用 需要安装gcc++环境 make # redis服启动添加对应参数 这样写还是挺麻烦的 # rebloom_module="/usr/local/rebloom/rebloom.so" # daemon --user ${REDIS_USER-redis} "$exec $REDIS_CONFIG --loadmodule # $rebloom_module --daemonize yes --pidfile $pidfile" # 记录当前位置 pwd # 进入reids目录 配置在redis.conf中 更加方便 vim redis.conf # :/loadmodule redisbloom.so是刚才具体的pwd位置 cv一下 loadmodule /xxx/redis/redis-5.0.8/RedisBloom-2.2.2/redisbloom.so # 保存退出 wq # 重新启动redis-server 我是在redis中 操作的 若不在请写出具体位置 不然会报错 redis-server redis.conf # 连接容器中的 redis 服务 若是无密码 redis-cli即可 redis-cli -a 密码 # 进入可以使用BF.ADD命令算成功
这篇关于缓存雪崩和缓存穿透的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-06-15matplotlib作图不显示3D图,怎么办?
- 2024-06-1503-Loki 日志监控
- 2024-06-1504-让LLM理解知识 -Prompt
- 2024-06-05做软件测试需要懂代码吗?
- 2024-06-0514-ShardingSphere的分布式主键实现
- 2024-06-03为什么以及如何要进行架构设计权衡?
- 2024-05-31全网首发第二弹!软考2024年5月《软件设计师》真题+解析+答案!(11-20题)
- 2024-05-31全网首发!软考2024年5月《软件设计师》真题+解析+答案!(21-30题)
- 2024-05-30【Java】百万数据excel导出功能如何实现
- 2024-05-30我们小公司,哪像华为一样,用得上IPD(集成产品开发)?