iagme.png
本文,你將閱讀到以下內容:
- 如何應對緩存擊穿和緩存雪崩的問題;
- Redis 的過期策略以及內存淘汰機制;
1.如何應對緩存擊穿和緩存雪崩的問題?
場景:在大并發情況下,流量幾百萬左右;
-
概念:
- 緩存穿透:故意去請求緩存中不存在的數據,導致所有的請求都懟到數據庫上,然而數據庫中也不存在,直接返回null,要是有人利用不存在的key頻繁攻擊我們的應用,那就有可能造成數據庫的崩潰;
- 緩存雪崩:設置緩存時采用了相同的過期時間,導致緩存在某一時刻同時失效,請求全部轉發到DB,DB瞬時壓力過重雪崩;
- 緩存擊穿:緩存中的某一個key在某個時間點過期的時候,恰好在這個時間點對這個Key有大量的并發請求過來,這些請求發現緩存過期一般都會從后端DB加載數據并回設到緩存,這個時候大并發的請求可能會瞬間把后端DB壓垮;
-
解決:
- 緩存穿透 or 緩存擊穿解決方案
- 布隆過濾器:將所有可能存在的數據哈希到一個足夠大的bitmap中,一個一定不存在的數據會被這個bitmap攔截掉,從而避免了對底層存儲系統的查詢壓力;
- 緩存null值:如果一個查詢返回的數據為空(不管是數 據不存在,還是系統故障),我們仍然把這個空結果進行緩存,但它的過期時間會很短,最長不超過五分鐘。
- 緩存雪崩解決方案:
- 緩存失效時間分散開來:可以在原有的失效時間基礎上增加一個隨機值,比如1-5分鐘隨機,這樣每一個緩存的過期時間的重復率就會降低,就很難引發集體失效的事件;
- 使用互斥鎖(該方案吞吐量明顯會下降):用加鎖或者隊列的方式保證緩存的單線 程(進程)寫,從而避免失效時大量的并發請求落到底層存儲系統上;
- 雙緩存:設計兩個緩存,緩存A和緩存B,緩存A的設置過期時間,緩存B不設失效時間,自己做緩存預熱操作;(可以細分下設計:從緩存A 讀數據,有則直接返回;A沒有數據,直接從B讀數據,直接返回,并且異步啟動一個更新線程,更新線程同時更新緩存A和緩存B);
- 緩存穿透 or 緩存擊穿解決方案
2.Redis 的過期策略以及內存淘汰機制
場景:
比如你的Redis只能存5G數據,可是你寫了10G,那會刪5G的數據,怎么刪的? 這個問題思考過么?
或者,你的數據已經設置了過期時間,但是時間到了,內存占用率還是比較高,有思考過原因么?-
概念
- 被動刪除:用到的時候才會去檢驗key是不是已過期,過期就刪除;
- 主動刪除:Redis會定期主動淘汰一批已過去的key;
-
原因:
- 定時刪除(主動刪除)
- 含義:在設置key的過期時間的同時,為該key創建一個定時器,讓定時器在key的過期時間來臨時,對key進行刪除;
- 優點:保證內存被盡快釋放;
- 缺點:若過期key很多,刪除這些key會占用很多的CPU時間,在CPU時間緊張的情況下,CPU不能把所有的時間用來做要緊的事兒,還需要去花時間刪除這些key定時器的創建耗時,若為每一個設置過期時間的key創建一個定時器(將會有大量的定時器產生)性能影響嚴重沒人用;
- 定期刪除(主動刪除)
- 含義:每隔一段時間執行一次刪除(在redis.conf配置文件設置hz,1s刷新的頻率)過期key操作
- 優點:通過限制刪除操作的時長和頻率,來減少刪除操作對CPU時間的占用--處理"定時刪除"的缺點定期刪除過期key--處理"惰性刪除"的缺點.
- 缺點:在內存友好方面,不如"定時刪除"在CPU時間友好方面,不如"惰性刪除"
- 難點:合理設置刪除操作的執行時長(每次刪除執行多長時間)和執行頻率(每隔多長時間做一次刪除)這個要根據服務器運行情況來定了.
- 惰性刪除(被動刪除)
- 含義:key過期的時候不刪除,每次從緩存中獲取key的時候去檢查是否過期,若過期,則刪除,返回null。
- 優點:刪除操作只發生在從數據庫取出key的時候發生,而且只刪除當前key,所以對CPU時間的占用是比較少的,而且此時的刪除是已經到了非做不可的地步(如果此時還不刪除的話,我們就會獲取到了已經過期的key了)
- 缺點:若大量的key在超出超時時間后,很久一段時間內,都沒有被獲取過,那么可能發生內存泄露(無用的垃圾占用了大量的內存);
- 定時刪除(主動刪除)
-
過期策略配置
- redis 內部默認采用的是定期刪除+惰性刪除策略;
- 定期刪除+惰性刪除流程:
- 惰性刪除流程:在進行get或setnx等操作時,先檢查key是否過期,若過期,刪除key,然后執行相應操作;若沒過期,直接執行相應操作;
- 定期刪除流程:遍歷每個數據庫(就是redis.conf中配置的"database"數量,默認為16),循環遍歷當前庫中的指定個數個key(默認是每個庫檢查20個key),如果當前庫中沒有一個key設置了過期時間,直接執行下一個庫的遍歷,隨機獲取一個設置了過期時間的key,檢查該key是否過期,如果過期,刪除key.判斷定期刪除操作是否已經達到指定時長(配置redis.conf 的hz選項),若已經達到,直接退出定期刪除. 也可以通過配置redis.conf的maxmemory最大值,當已用內存超過maxmemory限定時,就會觸發定期刪除策略;
- 定期刪除+惰性刪除流程:
- 過期key對持久化RDB和AOF的影響
- 從內存數據庫持久化數據到RDB文件,持久化key之前,會檢查是否過期,過期的key不進入RDB文件;
- 從RDB文件恢復數據到內存數據庫,在數據載入內存數據庫之前,會對key先進行過期檢查,如果過期,不導入數據庫.
- 從內存數據庫持久化數據到AOF文件,當key過期后,還沒有被刪除時,此時進行執行持久化操作(該key是不會進入aof文件的,因為沒有發生修改命令).
- 當key過期后,在發生刪除操作時,程序會向aof文件追加一條del命令(在將來的以aof文件恢復數據的時候該過期的鍵就會被刪掉);
- AOF重寫時,會先判斷key是否過期,已過期的key不會重寫到aof文件.
- 采用定期刪除+惰性刪除就沒其他問題了么?
- 并不是這樣的,如果定期刪除沒刪除Key,然后你也沒即時去請求Key,也就是說惰性刪除也沒生效.這樣,Redis的內存會越來越高.那么就應該采用內存淘汰機制.
- redis 內部默認采用的是定期刪除+惰性刪除策略;
-
內存淘汰機制
- 原因:Redis內存淘汰指的是用戶存儲的一些鍵被可以被它主動地從實例中刪除,從而產生讀miss的情況,內存的淘汰機制的初衷是為了更好地使用內存,用一定的緩存miss來換取內存的使用效率;
- 配置:
- Redis內存淘汰通過配置redis.conf中的maxmemory這個值來開啟,maxmemory為0的時候表示我們對Redis的內存使用沒有限制;
- 淘汰策略的選擇可以通過redis.conf中的maxmemory-policy來配置;
- 主鍵空間 && 設置了過期時間的鍵空間:在redis中,如果有一批鍵存儲在Redis中,則有那么一個哈希表用于存儲這批鍵及其值,如果這批鍵中有一部分設置了過期時間,那么這批鍵還會被存儲到另外一個哈希表中.設置了過期時間的鍵空間為主鍵空間的子集。
- 淘汰策略:
- noeviction:當redis內存數據達到maxmemory,在該策略下,新寫入數據時直接返回OOM錯誤;
- allkeys-lru:當內存不足以容納新寫入數據時,在主鍵空間中,優先移除最近未使用的一些key[以配置文件中的maxmemory-samples個key作為樣本池進行抽樣清理];
- allkeys-random:當內存不足以容納新寫入數據時,在主鍵空間中,隨機移除某些key[以配置文件中的maxmemory-samples個key作為樣本池進行抽樣清理];
- volatile-lru:當內存不足以容納新寫入數據時,在設置了過期時間的鍵空間中,移除最近最少使用的key[以配置文件中的maxmemory-samples個key作為樣本池進行抽樣清理],這種情況一般是把redis既當緩存,又做持久化存儲的時候才用.
- volatile-random:當內存不足以容納新寫入數據時,在設置了過期時間的鍵空間中,隨機移除某些key.
- volatile-ttl:當內存不足以容納新寫入數據時,在設置了過期時間的鍵空間中,有更早過期時間的key[以配置文件中的maxmemory-samples個key作為樣本池進行抽樣清理]優先移除;
- 注意:
- 淘汰策略的清理過程是阻塞的,直到清理出足夠的內存空間.所以如果在達到maxmemory并且調用方還在不斷寫入的情況下,可能會反復觸發主動清理策略,導致請求會有一定的延遲.
- 盡量不要觸發maxmemory,最好在mem_used內存占用達到maxmemory的一定比例后,需要考慮調大hz以加快淘汰,或者進行集群擴容.
- 如果能夠控制住內存,則可以不用修改maxmemory-samples配置;如果Redis本身就作為LRU cache服務(這種服務一般長時間處于maxmemory狀態,由Redis自動做LRU淘汰),可以適當調大maxmemory-samples.