Redis使用布隆過濾器解決緩存雪崩的問題
背景介紹
布隆過濾器可以幫助我們解決Redis緩存雪崩的問題,那什么是布隆過濾器、布隆過濾器又是如何使用如何解決緩存雪崩的問題的,讓我們帶著這一系列的問題去詳細了解布隆過濾器。
概念說明
布隆過濾器是一種用于快速判斷一個元素是否屬于一個集合的數(shù)據(jù)結(jié)構(gòu)。它通常用于大規(guī)模數(shù)據(jù)集合中,可以快速判斷一個元素是否可能存在于集合中,但不能確定一定存在。布隆過濾器的主要優(yōu)點是占用內(nèi)存少、查詢速度快,并且可以容忍一定的誤判率。
原理說明
布隆過濾器由一個位數(shù)組和多個哈希函數(shù)組成。位數(shù)組通常初始化為0,哈希函數(shù)用于將元素映射到位數(shù)組中的多個位置。當一個元素被加入到布隆過濾器中時,它會被哈希函數(shù)映射到位數(shù)組的多個位置,然后將這些位置的值設為1。當查詢一個元素是否存在于布隆過濾器中時,哈希函數(shù)會將元素映射到位數(shù)組的多個位置,然后檢查這些位置的值是否都為1,如果有一個位置的值為0,則可以確定元素一定不存在于集合中,如果所有位置的值都為1,則元素可能存在于集合中。
布隆過濾器的誤判率取決于位數(shù)組的大小和哈希函數(shù)的數(shù)量。通常情況下,誤判率隨著位數(shù)組大小的增加而減小,但會占用更多的內(nèi)存。因此,使用布隆過濾器時需要根據(jù)實際情況權衡誤判率和內(nèi)存占用。
解決穿透
我們還可以在存儲和緩存之前,加?個布隆過濾器,做?層過濾。布隆過濾器?會保存數(shù)據(jù)是否存在,如果判斷數(shù)據(jù)不存在,就不會訪問存儲。
安裝使用
安裝過程
Redis為普通安裝的配置方式
1、下載布隆過濾器這個插件
wget https://github.com/RedisLabsModules/rebloom/archive/v2.2.6.tar.gz
2、解壓文件
tar -zxvf v2.2.6.tar.gz
3、編輯插件
# 到RedisBloom對應目錄 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啟動的后容器中的文件,方便我們在容器外部操作redis的配置
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命令進行啟動
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
這個命令是用于在 Docker 中運行 Redis 容器,并進行一些配置。下面是對每個參數(shù)的解釋:
- -p 6379:6379: 將 Docker 容器的端口 6379 映射到主機的端口 6379,以便可以從主機訪問 Redis 服務。
- –name redis: 指定容器的名稱為 “redis”。
- -v /root/redis/data:/data: 將主機的 /root/redis/data 目錄掛載到容器的 /data 目錄,用于持久化保存 Redis 數(shù)據(jù)。
- -v /root/redis/redis.conf:/etc/redis/redis.conf: 將主機的 /root/redis/redis.conf 配置文件掛載到容器的 /etc/redis/redis.conf,使用該配置文件作為 Redis 的配置。
- –restart=always: 設置容器在退出時自動重新啟動。
- –network host: 使用主機網(wǎng)絡模式,容器將共享主機的網(wǎng)絡棧。
- -d: 在后臺運行容器。
- redis:5.0.7: 指定使用的 Redis 鏡像及其版本號。
- redis-server /etc/redis/redis.conf: 在容器中執(zhí)行的命令,即啟動 Redis 服務器,并使用指定的配置文件。
執(zhí)行上述操作redis容器如果啟動沒有問題那么我們的布隆過濾器的插件和redis都安裝并啟動成功了,如果沒有啟動成功可以通過docker logs 查看一下redis的啟動過程中出現(xiàn)什么問題。
具體使用
控制臺操作命令說明
- BF.ADD:向布隆過濾器中添加一個元素。
BF.ADD <key> <item>
- BF.EXISTS:檢查一個元素是否存在于布隆過濾器中。
BF.EXISTS <key> <item>
- -BF.MADD:向布隆過濾器中批量添加多個元素。
BF.MADD <key> <item> [item ...]
- BF.MEXISTS:批量檢查多個元素是否存在于布隆過濾器中。
BF.MEXISTS <key> <item> [item ...]
- BF.INFO:獲取布隆過濾器的信息,包括容量、誤判率等。
BF.INFO <key>
- BF.RESERVE:創(chuàng)建一個新的布隆過濾器,并指定容量和誤判率。
BF.RESERVE <key> <error_rate> <capacity>
- BF.COUNT:統(tǒng)計布隆過濾器中已添加的元素數(shù)量。
BF.COUNT <key>
給user過濾器添加一個元素,如果我們沒有添加創(chuàng)建布隆過濾器,系統(tǒng)會給我們創(chuàng)建一個,其中布隆過濾器的容量為100,判錯率為0.01這是布隆過濾器的默認配置,我們可以在創(chuàng)建布隆過濾器的時候進行修改。
Spring Boot集成布隆過濾器
1、引入依賴:這里使用的redis的過濾器所以用到的依賴直接使用的spring-data-redis這個就可以了。
<!--redis的依賴--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
2、布隆過濾器的工具類
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; // 初始化一個布隆過濾器 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); } // 查看某個元素是否是存在 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é)提升
布隆過濾器適用于需要快速判斷一個元素是否可能存在于集合中的場景,例如網(wǎng)絡爬蟲中的去重、緩存中的數(shù)據(jù)判斷等。但需要注意的是,布隆過濾器無法刪除元素,也無法準確地判斷一個元素是否存在于集合中,因此在一些場景下可能會產(chǎn)生誤判。
以上就是Redis使用布隆過濾器解決緩存雪崩的問題的詳細內(nèi)容,更多關于Redis布隆過濾器解決緩存雪崩的資料請關注腳本之家其它相關文章!
相關文章
通過docker和docker-compose安裝redis兩種方式詳解
這篇文章主要介紹了通過docker和docker-compose安裝redis的兩種方式,Docker安裝方式包括拉取鏡像、查看本地鏡像、運行容器和測試連接,Docker Compose安裝方式包括目錄結(jié)構(gòu)、配置文件、啟動和關閉容器、檢查啟動情況以及查看CPU和內(nèi)存使用狀態(tài),需要的朋友可以參考下2024-12-12Redis數(shù)據(jù)結(jié)構(gòu)之鏈表與字典的使用
這篇文章主要介紹了Redis數(shù)據(jù)結(jié)構(gòu)之鏈表與字典的使用,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2021-05-05淺談一下Redis的數(shù)據(jù)結(jié)構(gòu)
這篇文章主要介紹了淺談一下Redis的數(shù)據(jù)結(jié)構(gòu),簡單字符串結(jié)構(gòu)被用于存儲redis的key對象和String類型的value對象,其中的free和len字段可以輕松的使得在該字符串被修改時判斷是否需要擴容,需要的朋友可以參考下2023-08-08Unable?to?connect?to?Redis無法連接到Redis解決的全過程
這篇文章主要給大家介紹了關于Unable?to?connect?to?Redis無法連接到Redis解決的相關資料,文中通過圖文以及實例代碼將解決的過程介紹的非常詳細,需要的朋友可以參考下2023-03-03