起因:隨著項目的進一步推廣,數(shù)據(jù)量的增大,直接訪問mysql數(shù)據(jù)庫獲取數(shù)據(jù)所使用的時間越來越長,為解決當前主要矛盾,決定引入redis非關(guān)系型數(shù)據(jù)庫作為緩存層,使得數(shù)據(jù)并不能直接命中數(shù)據(jù)庫,減少訪問數(shù)據(jù)庫帶來的壓力,從而加快運行速度。
1. Redis緩存穿透解決方案
1.1. 緩存穿透的場景
get傳參數(shù),參數(shù)一般是id,如果這個id是一個無效id
String key = request.getParameter("key");
List<BuyCart> list = new ArrayList();
//習(xí)慣性會用json來保存結(jié)構(gòu)數(shù)據(jù)
String cartJson = redisOperator.get(key);
if(StringUtls.isBlank(cartJson)){
//redis里面沒有保存這個key
list = cartService.getCarts(key);
//從數(shù)據(jù)庫里查出來然后寫入redis
if(list!=null&&list.size()>0){
redisOperator.set("cartId:"+key,JsonUtils.objectToJson(list));
}else{
redisOperator.set("cartId:"+key,JsonUtils.objectToJson(list),10*60);
}
}else{
//redis里有值
list = JsonUtils.jsonToList(list));
}
假如我系統(tǒng)被人攻擊了,如何攻擊?
get傳參數(shù),傳N個無效的id
1.2. 布隆過濾器bloomfilter
之前講了一個hyperloglog,保存不重復(fù)的基數(shù)個數(shù):比如記錄訪問的UV,我只需要個數(shù)
場景描述
比如我們一個新聞客戶端,不斷的給用戶推薦新的新聞,推薦去重,還要高效
這個時候你想到Redis,能實時推送并快速去重
用戶A:1 2 3 4 5 6(2 7 9)每個用戶都應(yīng)該有一個瀏覽的歷史記錄:一旦時間長了,是不是數(shù)據(jù)量就非常大
如果用戶量也很大怎么辦?
這個時候我們的布隆過濾器就登場了
總結(jié)一下
- 布隆過濾器可以判斷數(shù)據(jù)是否存在
- 并且可以節(jié)省90%以上的存儲空間
- 但匹配精度會有一點不準確(涉及空間和時間的轉(zhuǎn)換:0.01%)
1.2.1. 布隆過濾器的運行場景
它本身是一個二進制的向量,存放的就是0,1
比如我們新建一個長度為16的布隆過濾器

所以布隆過濾器的精度是取決于bloom的存儲大小的的,如果長度越大,精度就越高
布隆過濾器的特征
- 精度是取決于bloom的存儲大小的的,如果長度越大,精度就越高
- 只能判斷數(shù)據(jù)是否一定不存在,而無法判斷數(shù)據(jù)是否一定存在
- bloom的存儲節(jié)點不能刪除,一旦刪除就影響其他節(jié)點數(shù)據(jù)的特征了
布隆過濾器存儲的節(jié)點數(shù)據(jù)一定是歷史數(shù)據(jù)
1.2.2. 布隆過濾器的使用
到入google的guava的POM
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>28.2-jre</version>
</dependency>
BloomFilter的代碼測試
public class BloomFilterTest {
public static void main(String[] args) {
//字符集,bf的存儲長度一般是你要存放的數(shù)據(jù)量1.5-2倍,期望的誤判率
BloomFilter bf = BloomFilter.create(Funnels.stringFunnel(Charset.forName("utf-8")),100000,0.0001);
for(int i=0;i<100000;i++){
bf.put(String.valueOf(i));
}
int flag = 0;
for(int i=100000;i<200000;i++){
if(bf.mightContain(String.valueOf(i))){
flag++;
}
}
System.out.println("誤判了:"+flag);
}
}
1.2.3. Redis集成布隆過濾器
Redis官方提供的布隆過濾器支持的是到了Redis4.x以后提供的插件功能
# 下載bloomfilter的插件
wget https://github.com/RedisLabsModules/rebloom/archive/v1.1.1.tar.gz
# 解壓
make
# 去到redis的配置文件對我們的過濾器進行添加
loadmodule /usr/local/software/RedisBloom-1.1.1/rebloom.so
# 創(chuàng)建bloomfilter并添加一個值
# 默認過濾器長度為100,精度0.01 : MBbloom
bf.add users value1 #可以不斷添加到同一個key
bf.madd users value1 value2
# 判斷一個值是否存在
bf.exists users value1
# 判斷多個值是否存在
bf.mexists users value1 value2 value3
# 手工建立bloomfliter的配置
bf.reserve userBM 0.001 10000
1.2.4. Java集成Redis BloomFilter
先導(dǎo)入依賴
<dependency>
<groupId>com.redislabs</groupId>
<artifactId>jrebloom</artifactId>
<version>1.2.0</version>
</dependency>
Java的bloomfilter調(diào)用
import io.rebloom.client.Client;
public class RedisBloomFilterTest {
public static void main(String[] args) {
Client bloomClient = new Client("127.0.0.1",6379);
//先創(chuàng)建bloomfilter
bloomClient.createFilter("userFilter",1000,0.001);
bloomClient.add("userFilter","gavin");
System.out.println("bloomfilter:"+bloomClient.exists("userFilter","gavin"));
}
}
1.2.5. 布隆過濾器的使用總結(jié)
- 布隆過濾器如果初始值過大會占用較大空間,過小會誤差率高,使用前估計好元素數(shù)量
- error_rate越小,占用空間就越大
- 只能判斷數(shù)據(jù)是否一定不存在,而無法判斷數(shù)據(jù)是否一定存在
- 可以節(jié)省90%的存儲空間
- 但匹配精度會有一點不準確(涉及空間和時間的轉(zhuǎn)換:0.01%)
- 布隆過濾器只能add和exists不能delete
help @generic
2. Redis雪崩解決方案
2.1. 什么是Redis雪崩
雪崩是基于數(shù)據(jù)庫,所有原理應(yīng)該到Redis的查詢?nèi)紻B,并且是同時到達
緩存在同一時間大量的key過期(key)
多個用戶同時請求并到達數(shù)據(jù),而且這個請求只有一個是有意義的,其他的都是重復(fù)無用功
2.2. Redis雪崩解決方案
- 緩存用不過期:冰封了
- 過期時間錯開(可以在key創(chuàng)建時加入一個1-10分鐘的隨機數(shù)給到key)
- 多緩存數(shù)據(jù)結(jié)合(不要直接打到DB上,可以在DB上再加一個搜索引擎)
- 在代碼里通過鎖解決(synchronized,分布式鎖zookeeper)
不要以為每天把功能完成了就行了,這種思想是要不得的,互勉~!