Redis6總綱
友情鏈接
http://www.lxweimin.com/p/cbec78a7761e
一、穿透
1、問題描述
key對應的數據在數據源并不存在,每次針對此key的請求從緩存獲取不到,請求都會壓到數據源,從而可能壓垮數據源。比如用一個不存在的用戶id獲取用戶信息,不論緩存還是數據庫都沒有,若黑客利用此漏洞進行攻擊可能壓垮數據庫。
2、解決方案
一個一定不存在緩存及查詢不到的數據,由于緩存是不命中時被動寫的,并且出于容錯考慮,如果從存儲層查不到數據則不寫入緩存,這將導致這個不存在的數據每次請求都要到存儲層去查詢,失去了緩存的意義。
解決方案:
(1)對空值緩存:如果一個查詢返回的數據為空(不管是數據是否不存在),我們仍然把這個空結果(null)進行緩存,設置空結果的過期時間會很短,最長不超過五分鐘
(2)設置可訪問的名單(白名單):
使用bitmaps類型定義一個可以訪問的名單,名單id作為bitmaps的偏移量,每次訪問和bitmap里面的id進行比較,如果訪問id不在bitmaps里面,進行攔截,不允許訪問。
(3)采用布隆過濾器:(布隆過濾器(Bloom Filter)是1970年由布隆提出的。它實際上是一個很長的二進制向量(位圖)和一系列隨機映射函數(哈希函數)。
布隆過濾器可以用于檢索一個元素是否在一個集合中。它的優點是空間效率和查詢時間都遠遠超過一般的算法,缺點是有一定的誤識別率和刪除困難。)
將所有可能存在的數據哈希到一個足夠大的bitmaps中,一個一定不存在的數據會被 這個bitmaps攔截掉,從而避免了對底層存儲系統的查詢壓力。
(4)進行實時監控:當發現Redis的命中率開始急速降低,需要排查訪問對象和訪問的數據,和運維人員配合,可以設置黑名單限制服務
二、擊穿
1、問題描述
key對應的數據存在,但在redis中過期,此時若有大量并發請求過來,這些請求發現緩存過期一般都會從后端DB加載數據并回設到緩存,這個時候大并發的請求可能會瞬間把后端DB壓垮。
2、解決方案
key可能會在某些時間點被超高并發地訪問,是一種非常“熱點”的數據。這個時候,需要考慮一個問題:緩存被“擊穿”的問題。
解決問題:
(1)預先設置熱門數據:在redis高峰訪問之前,把一些熱門數據提前存入到redis里面,加大這些熱門數據key的時長
(2)實時調整:現場監控哪些數據熱門,實時調整key的過期時長
(3)使用鎖:
(1) 就是在緩存失效的時候(判斷拿出來的值為空),不是立即去load db。
(2) 先使用緩存工具的某些帶成功操作返回值的操作(比如Redis的SETNX)去set一個mutex key
(3) 當操作返回成功時,再進行load db的操作,并回設緩存,最后刪除mutex key;
(4) 當操作返回失敗,證明有線程在load db,當前線程睡眠一段時間再重試整個get緩存的方法。
三、雪崩
1、問題描述
key對應的數據存在,但在redis中過期,此時若有大量并發請求過來,這些請求發現緩存過期一般都會從后端DB加載數據并回設到緩存,這個時候大并發的請求可能會瞬間把后端DB壓垮。
緩存雪崩與緩存擊穿的區別在于這里針對很多key緩存,前者則是某一個key
2、解決方案
緩存失效時的雪崩效應對底層系統的沖擊非常可怕!
解決方案:
(1)構建多級緩存架構:nginx緩存 + redis緩存 +其他緩存(ehcache等)
(2)使用鎖或隊列:
用加鎖或者隊列的方式保證來保證不會有大量的線程對數據庫一次性進行讀寫,從而避免失效時大量的并發請求落到底層存儲系統上。不適用高并發情況
(3)設置過期標志更新緩存:
記錄緩存數據是否過期(設置提前量),如果過期會觸發通知另外的線程在后臺去更新實際key的緩存。
(4)將緩存失效時間分散開:
比如我們可以在原有的失效時間基礎上增加一個隨機值,比如1-5分鐘隨機,這樣每一個緩存的過期時間的重復率就會降低,就很難引發集體失效的事件。
四、分布式鎖
1、問題描述
隨著業務發展的需要,原單體單機部署的系統被演化成分布式集群系統后,由于分布式系統多線程、多進程并且分布在不同機器上,這將使原單機部署情況下的并發控制鎖策略失效,單純的Java API并不能提供分布式鎖的能力。為了解決這個問題就需要一種跨JVM的互斥機制來控制共享資源的訪問,這就是分布式鎖要解決的問題!
分布式鎖主流的實現方案:
- 基于數據庫實現分布式鎖
- 基于緩存(Redis等)
- 基于Zookeeper
每一種分布式鎖解決方案都有各自的優缺點: - 性能:redis最高
- 可靠性:zookeeper最高
這里,我們就基于redis實現分布式鎖。
2、解決方案
(1)redis:命令:
setnx 設置鎖
127.0.0.1:6379> setnx username vv1
(integer) 1
127.0.0.1:6379> setnx username vv2
(integer) 0
127.0.0.1:6379> setnx username vv1
(integer) 0
127.0.0.1:6379> setnx username vv1
(integer) 0
del解除鎖
127.0.0.1:6379> del username
(integer) 1
127.0.0.1:6379> setnx username vv2
(integer) 1
127.0.0.1:6379> setnx username vv2
(integer) 0
127.0.0.1:6379> setnx username vv2
(integer) 0
expire設置過期時間,ttl查看過期時間
127.0.0.1:6379> expire username 19
(integer) 1
127.0.0.1:6379> ttl username
(integer) 14
127.0.0.1:6379> ttl username
(integer) 12
127.0.0.1:6379> ttl username
(integer) 11
127.0.0.1:6379> ttl username
(integer) 11
127.0.0.1:6379> ttl username
(integer) 9
ttl 為 -2 已經過期,-1 是永久生效
127.0.0.1:6379> ttl username
(integer) -2
127.0.0.1:6379> setnx username vv2
(integer) 1
127.0.0.1:6379> setnx username vv2
(integer) 0
127.0.0.1:6379> setnx username vv2
(integer) 0
★ 既上鎖又設置過期時間( nx 上鎖,ex 過期時間)
127.0.0.1:6379> set age 10 nx ex 11
OK
127.0.0.1:6379> ttl age
(integer) 7
127.0.0.1:6379> ttl age
(integer) 5