Redis学习补充与布隆过滤器

  • 一、缓存雪崩
    • 1、介绍
    • 2、处理雪崩数据
  • 二、缓存穿透
    • 1、介绍
    • 2、处理穿透数据
  • 三、缓存击穿
    • 2、处理击穿数据
  • 四、Redis其他知识
    • 1、Redis为何这么快
    • 2、Redis的持久化策略
  • 五、布隆过滤器
    • 1、安装
    • 2、介绍布隆过滤器
    • 3、使用布隆过滤器BloomFilter解决Redis的缓存穿透问题(方法一)
    • 4、使用布隆过滤器BloomFilter解决Redis的缓存穿透问题(方法二集合Lua脚本)
  • 七、其他知识

一、缓存雪崩

1、介绍

同一时间大面积失效,那一瞬间Redis跟没有一样

2、处理雪崩数据

在批量往Redis存数据的时候,把每个Key的失效时间都加个随机值就好了,这样可以保证数据不会在同一时间大面积失效。

setRedis(Key,value,time + Math.random() * 10000);

设置热点数据永远不过期,有更新操作就更新缓存就好了(比如运维更新了首页商品,那你刷下缓存就完事了,不要设置过期时间),电商首页的数据也可以用这个操作,保险。

二、缓存穿透

1、介绍

缓存穿透是指缓存和数据库中都没有的数据,而用户不断发起请求,我们数据库的 id 都是1开始自增上去的,如发起为id值为 -1 的数据或 id 为特别大不存在的数据。这时的用户很可能是攻击者,攻击会导致数据库压力过大,严重会击垮数据库。
如果不对参数做校验,数据库id都是大于0的,我一直用小于0的参数去请求你,每次都能绕开Redis直接打到数据库,数据库也查不到,每次都这样,并发高点就容易崩掉了。

2、处理穿透数据

缓存穿透我会在接口层增加校验,比如用户鉴权校验,参数做校验,不合法的参数直接代码Return,比如:id 做基础校验,id <=0的直接拦截等。
下面我有使用布隆过滤器解决穿透的案例

三、缓存击穿

缓存雪崩是因为大面积的缓存失效,打崩了DB,而缓存击穿不同的是缓存击穿是指一个Key非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个Key在失效的瞬间,持续的大并发就穿破缓存。

2、处理击穿数据

缓存击穿的话,设置热点数据永远不过期。或者加上互斥锁就能搞定

public static String getData(String key) throws InterruptedException {//从Redis查询数据String result = getDataByKV(key);//参数校验if (StringUtils.isBlank(result)) {try {//获得锁if (reenLock.tryLock()) {//去数据库查询result = getDataByDB(key);//校验if (StringUtils.isNotBlank(result)) {//插进缓存setDataToKV(key, result);}} else {//睡一会再拿Thread.sleep(100L);result = getData(key);}} finally {//释放锁reenLock.unlock();}}return result;}

四、Redis其他知识

1、Redis为何这么快

  1. Redis完全是基于内存的操作,CPU不是Redis的瓶颈,Redis的瓶颈最有可能是机器内存的大小或者网络带宽。
  2. 采用单线程,避免了不必要的上下文切换和竞争条件,不存在多线程导致的CPU切换,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有死锁问题导致的性能消耗。

2、Redis的持久化策略

Redis的持久化策略有两种:1、RDB:快照形式是直接把内存中的数据保存到一个dump的文件中,定时保存,保存策略。2、AOF:把所有的对Redis的服务器进行修改的命令都存到一个文件里,命令的集合。Redis默认是快照RDB的持久化方式。当Redis重启的时候,它会优先使用AOF文件来还原数据集,因为AOF文件保存的数据集通常比RDB文件所保存的数据集更完整。你甚至可以关闭持久化功能,让数据只在服务器运行时存。

五、布隆过滤器

1、安装

要redis4.0以上才支持

wget http://download.redis.io/releases/redis-4.0.14.tar.gz
tar xzf redis-4.0.14.tar.gz
mv redis-4.0.14 redis
cd redis
make
cd src
make install
#修改redis.conf配置
注释掉bind 127.0.0.1
daemonize yes
#设置密码password为密码
requirepass password
#指定配置文件启动服务
redis-server /home/installed/redis/redis.conf

其他

#修改redis启动端口
vim redis.conf
#修改内容
port 6378
pidfile /var/run/redis_6378.pid
#远程测试连接
redis-cli -h 112.17.192.219 -p 6378 auth 123321

安装布隆过滤器

#下载插件压缩包
wget https://github.com/RedisLabsModules/rebloom/archive/v1.1.1.tar.gz
#解压
tar -zxvf v1.1.1.tar.gz
#编译插件
cd RedisBloom-1.1.1/
make
#编译成功后看到redisbloom.so文件即可
#在redis.conf配置文件中加入如RedisBloom的redisbloom.so文件的地址
loadmodule /home/installed/RedisBloom-1.1.1/rebloom.so
#添加完成后需要重启redis
redis-server /home/installed/redis/redis.conf

测试

127.0.0.1:6379> bf.add users user2(integer) 1127.0.0.1:6379> bf.exists users user2(integer) 1127.0.0.1:6379> bf.exists users user3(integer) 0

2、介绍布隆过滤器

布隆过滤器的特点是判断不存在的,则一定不存在;判断存在的,大概率存在,但也有小概率不存在。并且这个概率是可控的,我们可以让这个概率变小或者变高,取决于用户本身的需求。

布隆过滤器由一个 bitSet 和 一组 Hash 函数(算法)组成,是一种空间效率极高的概率型算法和数据结构,主要用来判断一个元素是否在集合中存在。

在初始化时,bitSet 的每一位被初始化为0,同时会定义 Hash 函数,例如有3组 Hash 函数:hash1、hash2、hash3。
将 bitSet 的这3个下标标记为1。
在查找的时候也是根据得到的三个Hash所指向的位置是否为1.

3、使用布隆过滤器BloomFilter解决Redis的缓存穿透问题(方法一)

当一个查询请求过来时,先经过布隆过滤器进行查,如果判断请求查询值存在,则继续查;如果判断请求查询不存在,直接丢弃。

依赖

        <dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>21.0</version></dependency><!--redis设置--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><!--redis连接池--><dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId></dependency>

配置文件

server:port: 8000spring:redis:database:cache: 15 # cache索引token: 15 # Token索引mr: 15 # 病历索引host: 111.111.112.111  #Redis服务器地址port: 6379 # Redis服务器连接端口(本地环境端口6378,其他环境端口是6379)password: 123321 # Redis服务器连接密码(默认为空)lettuce:pool:max-active: 8 # 连接池最大连接数(使用负值表示没有限制)max-idle: 5 # 连接池中的最大空闲连接max-wait: -1 # 连接池最大阻塞等待时间(使用负值表示没有限制)min-idle: 0 # 连接池中的最小空闲连接timeout: 20000 # 连接超时时间(毫秒)

配置类

@Configuration
@EnableCaching
public class RedisConfig {@Beanpublic CacheManager cacheManager(RedisConnectionFactory connectionFactory) {RedisCacheManager rcm = RedisCacheManager.create(connectionFactory);return rcm;}@Beanpublic RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();redisTemplate.setConnectionFactory(factory);Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = newJackson2JsonRedisSerializer(Object.class);ObjectMapper om = new ObjectMapper();om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);jackson2JsonRedisSerializer.setObjectMapper(om);//序列化设置 ,这样计算是正常显示的数据,也能正常存储和获取redisTemplate.setKeySerializer(jackson2JsonRedisSerializer);redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);redisTemplate.setHashKeySerializer(jackson2JsonRedisSerializer);redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);return redisTemplate;}@Beanpublic StringRedisTemplate stringRedisTemplate(RedisConnectionFactory factory) {StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();stringRedisTemplate.setConnectionFactory(factory);return stringRedisTemplate;}//初始化布隆过滤器,放入到spring容器里面@Beanpublic BloomFilterHelper<String> initBloomFilterHelper() {return new BloomFilterHelper<>((Funnel<String>) (from, into) -> into.putString(from, Charsets.UTF_8).putString(from, Charsets.UTF_8), 1000000, 0.01);}}

工具类

public class BloomFilterHelper<T> {private int numHashFunctions;private int bitSize;private Funnel<T> funnel;/*** @param funnel* @param expectedInsertions* @param fpp*/public BloomFilterHelper(Funnel<T> funnel, int expectedInsertions, double fpp) {Preconditions.checkArgument(funnel != null, "funnel不能为空");this.funnel = funnel;// 计算bit数组长度bitSize = optimalNumOfBits(expectedInsertions, fpp);// 计算hash方法执行次数numHashFunctions = optimalNumOfHashFunctions(expectedInsertions, bitSize);}/*** @param value* @return*/public int[] murmurHashOffset(T value) {int[] offset = new int[numHashFunctions];long hash64 = Hashing.murmur3_128().hashObject(value, funnel).asLong();int hash1 = (int) hash64;int hash2 = (int) (hash64 >>> 32);for (int i = 1; i <= numHashFunctions; i++) {int nextHash = hash1 + i * hash2;if (nextHash < 0) {nextHash = ~nextHash;}offset[i - 1] = nextHash % bitSize;}return offset;}/*** 计算bit数组长度** @param n* @param p* @return*/private int optimalNumOfBits(long n, double p) {if (p == 0) {// 设定最小期望长度p = Double.MIN_VALUE;}int sizeOfBitArray = (int) (-n * Math.log(p) / (Math.log(2) * Math.log(2)));return sizeOfBitArray;}/*** 计算hash方法执行次数** @param n* @param m* @return*/private int optimalNumOfHashFunctions(long n, long m) {int countOfHash = Math.max(1, (int) Math.round((double) m / n * Math.log(2)));return countOfHash;}
}
@Service
public class RedisBloomFilter {@Autowiredprivate RedisTemplate redisTemplate;/*** 根据给定的布隆过滤器添加值*/public <T> void addByBloomFilter(BloomFilterHelper<T> bloomFilterHelper, String key, T value) {Preconditions.checkArgument(bloomFilterHelper != null, "bloomFilterHelper不能为空");//就是把我们需要存入的value,通过算法计算出相关需要绑定 1的 bit位  的数组。int[] offset = bloomFilterHelper.murmurHashOffset(value);for (int i : offset) {System.out.println("key : " + key + " " + "value : " + i);//就是将计算完得到的bit位数组,存入redis里面的bit结构里面,i就是数组内的bit位位置,每个都设置为true。redisTemplate.opsForValue().setBit(key, i, true);}}/*** 根据给定的布隆过滤器判断值是否存在*/public <T> boolean includeByBloomFilter(BloomFilterHelper<T> bloomFilterHelper, String key, T value) {Preconditions.checkArgument(bloomFilterHelper != null, "bloomFilterHelper不能为空");int[] offset = bloomFilterHelper.murmurHashOffset(value);//循环遍历得到的所有值,判断是否都被标记for (int i : offset) {System.out.println("key : " + key + " " + "value : " + i);if (!redisTemplate.opsForValue().getBit(key, i)) {return false;}}return true;}}

测试类

@RestController
public class BloomFilterController {@AutowiredRedisBloomFilter redisBloomFilter;@Autowiredprivate BloomFilterHelper bloomFilterHelper;@RequestMapping("/add")public String addBloomFilter(@RequestParam("orderNum") String orderNum) {try {redisBloomFilter.addByBloomFilter(bloomFilterHelper, "bloom", orderNum);} catch (Exception e) {e.printStackTrace();return "添加失败";}return "添加成功";}@RequestMapping("/check")public boolean checkBloomFilter(@RequestParam("orderNum") String orderNum) {boolean result = redisBloomFilter.includeByBloomFilter(bloomFilterHelper, "bloom", orderNum);return result;}
}

打印出来的这些就是“sb”产生的Hash,我们逻辑用其标记redis相应的位置。

效果图


4、使用布隆过滤器BloomFilter解决Redis的缓存穿透问题(方法二集合Lua脚本)

这个我个人比较喜欢,前提是redis4.0以上版本。

代码块

@RestController
public class RedisController {@Autowiredprivate RedisTemplate<String, String> redisTemplate;@RequestMapping("/lua/{id}")public String sendLua(@PathVariable String id) {//添加key值String script = "return redis.call('bf.add',KEYS[1],ARGV[1])";DefaultRedisScript<Boolean> redisScript = new DefaultRedisScript<>(script, Boolean.class);Boolean result1 = redisTemplate.execute(redisScript, Collections.singletonList("my_bloom_one"), String.valueOf("user" + id));System.out.println(result1);//判断是否存在String scriptEx = "return redis.call('bf.exists',KEYS[1],ARGV[1])";DefaultRedisScript<Boolean> redisScript1 = new DefaultRedisScript<>(scriptEx, Boolean.class);Boolean result2 = redisTemplate.execute(redisScript1, Collections.singletonList("my_bloom_one"), String.valueOf("user" + id));System.out.println(result2);return "添加是否成功"+result1+"====================="+"查询是否存在"+result2;}}


再次创建时

redis程序中已成功创建

七、其他知识

设置过期时间(第三个参数表示单位为秒)

stringRedisTemplate.expire("baike",10 , TimeUnit.SECONDS);

添加单个元素(redis命令)

BF.ADD newFilter foo

添加 并检查多个元素

//添加已存在的元素返回0
BF.MADD myFilter foo bar baz

检查 过滤器中是否存在该元素

BF.EXISTS newFilter foo

批量检查 过滤器中是否存在该元素

BF.MEXISTS newFilter foo bar bbb

设置过滤器的错误率和储存量

//key  :需要设置的键,必须是不存在的键
//error_rate:允许的错误率 0.0001等  默认值:0.01
//size:保存的数组大小,尽可能设大一些,防止不够用。默认值:100
BF.RESERVE  <key> <error_rate> <size>

Redis雪崩、穿透、击穿补充学习与布隆过滤器相关推荐

  1. Redis 5.0.8+常见面试题(单线程还是多线程、先更新缓存还是数据库、雪崩穿透击穿解决办法...)

    Redis 6.0 保姆级教程(含微服务案例与完整面试题):https://www.yuque.com/yuxuandmbjz/redis Redis是单线程还是多线程 ?为什么这么设计 ? Redi ...

  2. 【redis】缓存预热雪崩穿透击穿

    [redis]缓存预热雪崩穿透击穿(上) 文章目录 [redis]缓存预热雪崩穿透击穿(上) 前言 一.面试题 二.缓存预热 三.缓存雪崩 发生原因 预防+解决 高可用: 多缓存结合: 人民币玩家 四 ...

  3. 老生常谈的 Redis 雪崩、击穿、穿透、预热、降级一次全安排

    △Hollis, 一个对Coding有着独特追求的人△ 这是Hollis的第 348 篇原创分享 作者 l zyz1992 来源 l Hollis(ID:hollischuang) 关于 Redis ...

  4. Java面试核心知识:Redis 雪崩、击穿、穿透、预热、降级,一文带你全部学会

    文章目录 Java面试核心知识点 Spring原理及应用 spring CIoud原理及应用 Netty网络编程原理及应用 Zookeeper原理及应用 kafka原理及应用 Hadoop原理及应用 ...

  5. Redis缓存穿透击穿雪崩

    目录 1.缓存穿透 2.缓存击穿 3.缓存雪崩 1.缓存穿透 概述: 缓存穿透的概念很简单,用户想要査询一个数据,发现redis内存数据库没有,也就是缓存没有命中,于是向持久层数据库査询.发现也数据库 ...

  6. Redis缓存穿透击穿和雪崩(八)

    1. 缓存穿透 1.1. 定义 如果用户的请求Redis缓存没有,mysql持久层也没有这个数据,于是本地查询失败.当用户请求很多(或者恶意攻击)且都是这种缓存和持久层都没有命中的情况时,大量的请求持 ...

  7. Redis缓存/穿透/击穿/雪崩

    目录 1 缓存穿透 1.1 问题描述 1.2 产生原因 1.3 解决方案 2 缓存击穿 2.1 问题描述 2.2 解决方案 3 缓存雪崩 3.1 问题描述 3.2 解决方案: 1 缓存穿透 1.1 问 ...

  8. 5redis-----------redis高级--GEO-查询附近的人、基数统计算法HLL 、布隆过滤器、缓存雪崩穿透击穿-------全栈式开发44

    redis高级 一.GEO查询附近的人 二.基数统计算法-HyperLogLog 三.布隆过滤器 四.缓存雪崩&缓存穿透 (一)缓存雪崩 (二)缓存穿透 (三)缓存击穿 一.GEO查询附近的人 ...

  9. 一文搞懂Redis缓存穿透/击穿/雪崩

    缓存穿透 问题描述 缓存穿透是指查询一个一定不存在的数据,由于缓存时不命中的,则需要从数据库中查询.查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到数据库中去查询,进而增大了数据库的压力 ...

  10. 关于缓存雪崩\穿透\击穿等一些问题

    前言 设计一个缓存系统,不得不要考虑的问题就是:缓存穿透.缓存击穿与失效时的雪崩效应. 缓存穿透 缓存穿透是指查询一个一定不存在的数据,由于缓存是不命中时被动写的,并且出于容错考虑,如果从存储层查不到 ...

最新文章

  1. Swift2.0语言教程之闭包
  2. 解决IE更新对FLASH产生影响
  3. python怎么安装matplotlib-为python安装matplotlib模块
  4. C语言复习2_运算符
  5. K-均值对地图上的点进行聚类(2)
  6. 2019-1-92.4G射频芯片培训资料
  7. docker copy异常
  8. android简单课表,模仿大神的一个android课程表
  9. 买了小区一楼的感受是怎样的?
  10. 如果降低sbus总线的传输速率/帧率,sbus怎样转UART,sbus接到4G
  11. [Python] L1-019. 谁先倒-PAT团体程序设计天梯赛GPLT
  12. 挑战IPOD功能最强MP3——Zune二代评测
  13. 机器学习基石笔记2——在何时可以使用机器学习(2)
  14. oracle通过数据泵导出数据,Oracle通过PL/SQL数据泵导出导入数据的命令
  15. python二进制转换为16进制
  16. MRT工具谢幕,HEG华丽登场
  17. 计算机更新配置卡住了,win10更新设置卡死怎么办|win10更新设置卡死的完美解决方法...
  18. <<视觉问答>>2022:CLIP Models are Few-shot Learners: Empirical Studies on VQA and Visual Entailment
  19. 汇编语言的一些相关资料(上机或者实验)
  20. 香港城市大学赵翔宇招收AI机器学习数据挖掘 全奖PhD/博后/RA

热门文章

  1. 迈思德物联网网关与施耐德SoMachine平台远程调试测试成功。
  2. 面试官:前端布局了解嘛?我一下说了接近五十种布局方案,给面试官整不会了。
  3. JavaScript中 切割截取字符串的几种方法
  4. 2021年烷基化工艺考试内容及烷基化工艺考试资料
  5. Xshell7绿色版使用公钥对免密连接虚拟机(centos7.9-保姆级)
  6. NSGA_2 Matlab带约束问题的多目标优化求解方案+惩罚函数
  7. 常见四大类型视频接口
  8. NOSQL,MongoDB是什么?
  9. 数据库文档生成工具-markdown格式
  10. 并联机构工作空间求解_结构计算软件—结构力学求解器