redis 缓存穿透穿透解决方案 - 布隆过滤器
咱们先来看一段代码
cache_key = "id:1"
cache_value = GetValueFromRedis(cache_key); // 判断缓存是否有数据
if cache_value != nil{ // 如果有 间接返回数据
return cache_value
}
db_value = GetValueFromDb(cache_key) // 从数据库中查问数据
if db_value == nil{return db_value}
expire_time = 300
SetRedisValue(cache_key, db_value, expire_time) // 将数据库的后果更新到缓存中,并间接返回后果
return db_value
置信绝大多数同学都是这么解决申请的,这样用 redis 可能给 mysql 抵御住大部分的申请。其实这样是存在肯定的问题的
问题 1
我在申请的时候, 用 id=- 1 来申请
id=- 1 这条记录在数据库中是不存在的,当然对应的 redis 中也是没有的。那么就须要去申请数据库而后把数据写入到 redis 中,这样就会造成没有必要的数据库申请,一两个申请无所谓,然而如果从 -∞到 -1 有限的高频率的申请,就会给线上造成很大的压力。
针对问题 1 的解决方案
咱们能够通过程序来限度 id 的合法性,判断 id<1 的状况都间接在接口层面拦挡,这个形式确实能够解决下面说的那种状况,然而咱们接下来往下看
问题 2
比方当初数据库 id 的最大值为 1000,咱们用比 1000 大的数字去申请
这种状况原理和问题 1 是一样的,这次咱们就没法通过参数判断来拦挡住申请了,所以咱们就得用接下来一种经典的形式,布隆过滤器
布隆过滤器其实就是一种比拟奇妙的概率型数据结构,它能够通知你某种货色肯定不存在或者可能存在。从而达到对脏数据过滤的成果。他存在的地位如图
其实对布隆过滤器比拟生疏的同学能够先想想,作为一个过滤器须要满足什么条件?
- 速度得快,得从内存查,如果从硬盘查的话还不如间接查数据库
- 因为过滤器外面得存入数据库所有的数据,所以内存势必是比拟缓和的,所以内存要做到相对的节俭,说到节俭内存,大家应该很容易能想到 redis 外面的 setbit 操作
布隆过滤器的实现
写入过程
- 通过 bit 数组来标识数据
- 比方 id=10 的数据,通过 hash 算法算进去后果为 1
- 把 bit 数组下表为 1 的地位的值标记为 1
查问过程
- 将 id=10 做 hash 运算,失去后果 1
- 看 bit 数组下表为 1 的数据标识为 1,则阐明数据存在
其实咱们看下面的算法是存在肯定的问题的
1: 只有是 hash 运算,就会存在 hash 碰撞问题,比方 id=10 和 id=100 可能通过 hash 运算之后后果都为 1,那么 id=10 写入之后查问 id=100 是否存在会误判为 id=100 也存在
2: 当 bit 数组满了之后,查问的错误率必定是百分之百,因为每个数据都存在
这些其实都是导致错误率的起因,错误率是不可能防止的,然而咱们能够缩小错误率,缩小错误率的办法有两个
1: 加大 bit 数组的长度,对于 bit 数组的长度的减少是不必放心的,因为是 bit 操作,所以能够加到很大的值
2: 减少 hash 函数的个数,hash 函数的个数减少了,阐明标识一个数组须要的地位就会变多。这样会升高产生 hash 碰撞的概率。然而 hash 的函数也不是越多越好,须要参照数组的长度来定
hash 错误率:
布隆算法说数据存在,那么理论有可能不存在
如果数据不存在。那么肯定不存在
布隆过滤器 redis 中的应用办法
1. 下载 redisbloom 插件(redis 官网下载即可)
wget https://github.com/RedisLabsModules/rebloom/archive/v1.1.1.tar.gz
2: 解压并装置,生成.so 文件
[root@redis]# tar -zxvf v1.1.1.tar.gz
[root@redis]# cd Redisbloom-1.1.1/
[root@redisbloom-1.1.1]# make
[root@redisbloom-1.1.1]# ls
contrib Dockerfile docs LICENSE Makefile mkdocs.yml ramp.yml README.md rebloom.so src tests
3: 在 redis 配置文件 (redis.conf) 中退出该模块即可
[root@redis]# vim redis.conf
#####################MODULES################# # Load modules at startup. If the server is not able to load modules
# it will abort. It is possible to use multiple loadmodule directives.
loadmodule /usr/local/redis/redisbloom-1.1.1/rebloom.so
4: 重新启动 redis
redis-server ./redis.conf
5: 测试装置是否胜利
127.0.0.1:6379> bf.add users user2 // 写入数据 user2
(integer) 1
127.0.0.1:6379> bf.add users user1 // 写入数据 user1
(integer) 1
127.0.0.1:6379> bf.exists users user1 // 查问 user1 存在
(integer) 1
127.0.0.1:6379> bf.exists users user3 // 查问 user3 不存在
(integer) 0
下面说过布隆过滤器存在误判的状况,在 redis 中有两个值决定布隆过滤器的准确率:
- error_rate:容许布隆过滤器的错误率,这个值越低过滤器的位数组的大小越大,占用空间也就越大。
- initial_size:布隆过滤器能够贮存的元素个数,当理论存储的元素个数超过这个值之后,过滤器的准确率会降落。
redis 中有一个命令能够来设置这两个值:
bf.reserve users 0.01 100
三个参数的含意:
第一个值是过滤器的名字。
第二个值为 error_rate 的值。
第三个值为 initial_size 的值。
关注我的技术公众号,每周都有优质技术文章推送。
微信扫一扫下方二维码即可关注: