Redis使用布隆過(guò)濾器解決緩存雪崩的問(wèn)題
背景介紹
布隆過(guò)濾器可以幫助我們解決Redis緩存雪崩的問(wèn)題,那什么是布隆過(guò)濾器、布隆過(guò)濾器又是如何使用如何解決緩存雪崩的問(wèn)題的,讓我們帶著這一系列的問(wèn)題去詳細(xì)了解布隆過(guò)濾器。
概念說(shuō)明
布隆過(guò)濾器是一種用于快速判斷一個(gè)元素是否屬于一個(gè)集合的數(shù)據(jù)結(jié)構(gòu)。它通常用于大規(guī)模數(shù)據(jù)集合中,可以快速判斷一個(gè)元素是否可能存在于集合中,但不能確定一定存在。布隆過(guò)濾器的主要優(yōu)點(diǎn)是占用內(nèi)存少、查詢速度快,并且可以容忍一定的誤判率。
原理說(shuō)明
布隆過(guò)濾器由一個(gè)位數(shù)組和多個(gè)哈希函數(shù)組成。位數(shù)組通常初始化為0,哈希函數(shù)用于將元素映射到位數(shù)組中的多個(gè)位置。當(dāng)一個(gè)元素被加入到布隆過(guò)濾器中時(shí),它會(huì)被哈希函數(shù)映射到位數(shù)組的多個(gè)位置,然后將這些位置的值設(shè)為1。當(dāng)查詢一個(gè)元素是否存在于布隆過(guò)濾器中時(shí),哈希函數(shù)會(huì)將元素映射到位數(shù)組的多個(gè)位置,然后檢查這些位置的值是否都為1,如果有一個(gè)位置的值為0,則可以確定元素一定不存在于集合中,如果所有位置的值都為1,則元素可能存在于集合中。
布隆過(guò)濾器的誤判率取決于位數(shù)組的大小和哈希函數(shù)的數(shù)量。通常情況下,誤判率隨著位數(shù)組大小的增加而減小,但會(huì)占用更多的內(nèi)存。因此,使用布隆過(guò)濾器時(shí)需要根據(jù)實(shí)際情況權(quán)衡誤判率和內(nèi)存占用。
解決穿透
我們還可以在存儲(chǔ)和緩存之前,加?個(gè)布隆過(guò)濾器,做?層過(guò)濾。布隆過(guò)濾器?會(huì)保存數(shù)據(jù)是否存在,如果判斷數(shù)據(jù)不存在,就不會(huì)訪問(wèn)存儲(chǔ)。
安裝使用
安裝過(guò)程
Redis為普通安裝的配置方式
1、下載布隆過(guò)濾器這個(gè)插件
wget https://github.com/RedisLabsModules/rebloom/archive/v2.2.6.tar.gz
2、解壓文件
tar -zxvf v2.2.6.tar.gz
3、編輯插件
# 到RedisBloom對(duì)應(yīng)目錄 cd /usr/local/redis/RedisBloom-2.2.6 # 編譯插件 make
4、Redis集成RedisBloom插件
# vim查看redis.conf vim /usr/local/redis/config/redis.conf # 在文件后面添加如下配置 loadmodule /usr/local/redis/RedisBloom-2.2.6/redisbloom.so
5、配置完之后重啟Redis即可。
Redis為Docker鏡像安裝的配置方式
1、創(chuàng)建文件夾以及配置文件,用于掛在redis啟動(dòng)的后容器中的文件,方便我們?cè)谌萜魍獠坎僮鱮edis的配置
mkdir data ##創(chuàng)建文件夾 touch redis.conf ## 創(chuàng)建文件
2、在我們創(chuàng)建的redis.conf文件中添加一行配置loadmodule /data/RedisBloom-2.2.6/redisbloom.so
3、隨后直接使用dokcer run命令進(jìn)行啟動(dòng)
docker run -p 6379:6379 --name redis -v /root/redis/data:/data -v /root/redis/redis.conf:/etc/redis/redis.conf --restart=always --network host -d redis:5.0.7 redis-server /etc/redis/redis.conf
這個(gè)命令是用于在 Docker 中運(yùn)行 Redis 容器,并進(jìn)行一些配置。下面是對(duì)每個(gè)參數(shù)的解釋:
- -p 6379:6379: 將 Docker 容器的端口 6379 映射到主機(jī)的端口 6379,以便可以從主機(jī)訪問(wèn) Redis 服務(wù)。
- –name redis: 指定容器的名稱為 “redis”。
- -v /root/redis/data:/data: 將主機(jī)的 /root/redis/data 目錄掛載到容器的 /data 目錄,用于持久化保存 Redis 數(shù)據(jù)。
- -v /root/redis/redis.conf:/etc/redis/redis.conf: 將主機(jī)的 /root/redis/redis.conf 配置文件掛載到容器的 /etc/redis/redis.conf,使用該配置文件作為 Redis 的配置。
- –restart=always: 設(shè)置容器在退出時(shí)自動(dòng)重新啟動(dòng)。
- –network host: 使用主機(jī)網(wǎng)絡(luò)模式,容器將共享主機(jī)的網(wǎng)絡(luò)棧。
- -d: 在后臺(tái)運(yùn)行容器。
- redis:5.0.7: 指定使用的 Redis 鏡像及其版本號(hào)。
- redis-server /etc/redis/redis.conf: 在容器中執(zhí)行的命令,即啟動(dòng) Redis 服務(wù)器,并使用指定的配置文件。
執(zhí)行上述操作redis容器如果啟動(dòng)沒(méi)有問(wèn)題那么我們的布隆過(guò)濾器的插件和redis都安裝并啟動(dòng)成功了,如果沒(méi)有啟動(dòng)成功可以通過(guò)docker logs 查看一下redis的啟動(dòng)過(guò)程中出現(xiàn)什么問(wèn)題。
具體使用
控制臺(tái)操作命令說(shuō)明
- BF.ADD:向布隆過(guò)濾器中添加一個(gè)元素。
BF.ADD <key> <item>
- BF.EXISTS:檢查一個(gè)元素是否存在于布隆過(guò)濾器中。
BF.EXISTS <key> <item>
- -BF.MADD:向布隆過(guò)濾器中批量添加多個(gè)元素。
BF.MADD <key> <item> [item ...]
- BF.MEXISTS:批量檢查多個(gè)元素是否存在于布隆過(guò)濾器中。
BF.MEXISTS <key> <item> [item ...]
- BF.INFO:獲取布隆過(guò)濾器的信息,包括容量、誤判率等。
BF.INFO <key>
- BF.RESERVE:創(chuàng)建一個(gè)新的布隆過(guò)濾器,并指定容量和誤判率。
BF.RESERVE <key> <error_rate> <capacity>
- BF.COUNT:統(tǒng)計(jì)布隆過(guò)濾器中已添加的元素?cái)?shù)量。
BF.COUNT <key>
給user過(guò)濾器添加一個(gè)元素,如果我們沒(méi)有添加創(chuàng)建布隆過(guò)濾器,系統(tǒng)會(huì)給我們創(chuàng)建一個(gè),其中布隆過(guò)濾器的容量為100,判錯(cuò)率為0.01這是布隆過(guò)濾器的默認(rèn)配置,我們可以在創(chuàng)建布隆過(guò)濾器的時(shí)候進(jìn)行修改。
Spring Boot集成布隆過(guò)濾器
1、引入依賴:這里使用的redis的過(guò)濾器所以用到的依賴直接使用的spring-data-redis這個(gè)就可以了。
<!--redis的依賴--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
2、布隆過(guò)濾器的工具類(lèi)
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.script.DefaultRedisScript; import org.springframework.data.redis.core.script.RedisScript; import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; import java.util.Collections; import java.util.List; import java.util.stream.Collectors; @Component public class RedisBloomUtil { @Autowired private RedisTemplate redisTemplate; // 初始化一個(gè)布隆過(guò)濾器 public Boolean tryInitBloomFilter(String key, long expectedInsertions, double falseProbability) { Boolean keyExist = redisTemplate.hasKey(key); if(keyExist) { return false; } RedisScript<Boolean> script = new DefaultRedisScript<>(bloomInitLua(), Boolean.class); RedisSerializer stringSerializer = redisTemplate.getStringSerializer(); redisTemplate.execute(script, stringSerializer, stringSerializer, Collections.singletonList(key), falseProbability+"", expectedInsertions+""); return true; } // 添加元素 public Boolean addInBloomFilter(String key, Object arg) { RedisScript<Boolean> script = new DefaultRedisScript<>(addInBloomLua(), Boolean.class); return (Boolean) redisTemplate.execute(script, Collections.singletonList(key), arg); } @Transactional // 批量添加元素 public Boolean batchAddInBloomFilter(String key, Object... args) { RedisScript<Boolean> script = new DefaultRedisScript<>(batchAddInBloomLua(), Boolean.class); return (Boolean) redisTemplate.execute(script, Collections.singletonList(key), args); } // 查看某個(gè)元素是否是存在 public Boolean existInBloomFilter(String key, Object arg) { RedisScript<Boolean> script = new DefaultRedisScript<>(existInBloomLua(), Boolean.class); return (Boolean) redisTemplate.execute(script, Collections.singletonList(key), arg); } // 批量查看元素是否存在 public List batchExistInBloomFilter(String key, Object... args) { RedisScript<List> script = new DefaultRedisScript(batchExistInBloomLua(), List.class); List<Long> results = (List) redisTemplate.execute(script, Collections.singletonList(key), args); List<Boolean> booleanList = results.stream().map(res -> res == 1 ? true : false).collect(Collectors.toList()); return booleanList; } private String bloomInitLua() { return "redis.call('bf.reserve', KEYS[1], ARGV[1], ARGV[2])"; } private String addInBloomLua() { return "return redis.call('bf.add', KEYS[1], ARGV[1])"; } private String batchAddInBloomLua() { StringBuilder sb = new StringBuilder(); sb.append("for index, arg in pairs(ARGV)").append("\r\n"); sb.append("do").append("\r\n"); sb.append("redis.call('bf.add', KEYS[1], arg)").append("\r\n"); sb.append("end").append("\r\n"); sb.append("return true"); return sb.toString(); } private String existInBloomLua() { return "return redis.call('bf.exists', KEYS[1], ARGV[1])"; } private String batchExistInBloomLua() { StringBuilder sb = new StringBuilder(); sb.append("local results = {}").append("\r\n"); sb.append("for index, arg in pairs(ARGV)").append("\r\n"); sb.append("do").append("\r\n"); sb.append("local exist = redis.call('bf.exists', KEYS[1], arg)").append("\r\n"); sb.append("table.insert(results, exist)").append("\r\n"); sb.append("end").append("\r\n"); sb.append("return results;"); return sb.toString(); } }
總結(jié)提升
布隆過(guò)濾器適用于需要快速判斷一個(gè)元素是否可能存在于集合中的場(chǎng)景,例如網(wǎng)絡(luò)爬蟲(chóng)中的去重、緩存中的數(shù)據(jù)判斷等。但需要注意的是,布隆過(guò)濾器無(wú)法刪除元素,也無(wú)法準(zhǔn)確地判斷一個(gè)元素是否存在于集合中,因此在一些場(chǎng)景下可能會(huì)產(chǎn)生誤判。
以上就是Redis使用布隆過(guò)濾器解決緩存雪崩的問(wèn)題的詳細(xì)內(nèi)容,更多關(guān)于Redis布隆過(guò)濾器解決緩存雪崩的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
通過(guò)docker和docker-compose安裝redis兩種方式詳解
這篇文章主要介紹了通過(guò)docker和docker-compose安裝redis的兩種方式,Docker安裝方式包括拉取鏡像、查看本地鏡像、運(yùn)行容器和測(cè)試連接,Docker Compose安裝方式包括目錄結(jié)構(gòu)、配置文件、啟動(dòng)和關(guān)閉容器、檢查啟動(dòng)情況以及查看CPU和內(nèi)存使用狀態(tài),需要的朋友可以參考下2024-12-12redis事務(wù)_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
這篇文章主要介紹了redis事務(wù),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-08-08Redis在項(xiàng)目中的使用(JedisPool方式)
項(xiàng)目操作redis是使用的RedisTemplate方式,另外還可以完全使用JedisPool和Jedis來(lái)操作redis,本文給大家介紹Redis在項(xiàng)目中的使用,JedisPool方式,感興趣的朋友跟隨小編一起看看吧2021-12-12Redis數(shù)據(jù)結(jié)構(gòu)之鏈表與字典的使用
這篇文章主要介紹了Redis數(shù)據(jù)結(jié)構(gòu)之鏈表與字典的使用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-05-05淺談一下Redis的數(shù)據(jù)結(jié)構(gòu)
這篇文章主要介紹了淺談一下Redis的數(shù)據(jù)結(jié)構(gòu),簡(jiǎn)單字符串結(jié)構(gòu)被用于存儲(chǔ)redis的key對(duì)象和String類(lèi)型的value對(duì)象,其中的free和len字段可以輕松的使得在該字符串被修改時(shí)判斷是否需要擴(kuò)容,需要的朋友可以參考下2023-08-08Unable?to?connect?to?Redis無(wú)法連接到Redis解決的全過(guò)程
這篇文章主要給大家介紹了關(guān)于Unable?to?connect?to?Redis無(wú)法連接到Redis解決的相關(guān)資料,文中通過(guò)圖文以及實(shí)例代碼將解決的過(guò)程介紹的非常詳細(xì),需要的朋友可以參考下2023-03-03Redis高可用部署架構(gòu)的實(shí)現(xiàn)
本文主要介紹了Redis高可用部署架構(gòu)的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-08-08完美解決linux上啟動(dòng)redis后配置文件未生效的問(wèn)題
今天小編就為大家分享一篇完美解決linux上啟動(dòng)redis后配置文件未生效的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-05-05Redis?抽獎(jiǎng)大轉(zhuǎn)盤(pán)的實(shí)戰(zhàn)示例
本文主要介紹了Redis?抽獎(jiǎng)大轉(zhuǎn)盤(pán)的實(shí)戰(zhàn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-12-12