當前位置:首頁 » 文件管理 » redis緩存失效策略

redis緩存失效策略

發布時間: 2022-11-27 02:40:24

A. Redis持久化策略(看這篇,你肯定會有所獲)

RDB:Redis DataBase , 記錄快照

        RDB是redis 默認的持久化方案. RDB 是當滿足一定條件時, 就會將redis內存中的數據寫入磁碟,並生成一個快照文件mp.rdb 文件.Redis 重啟會通過載入mp.rdb文件恢復數據.

        一定條件分為以下幾種情況: 1.自動觸發  2. 手動觸發 . 下面分開說明下:

a).redis.conf 中 SNAPSHOTTING 其中定義了觸發把數據保存到磁碟的觸發頻率.

        如果不需要rdb 方案, 注釋save 或者配置成空字元串" ".

        save 900 1 #900秒內至少有一個key被修改(包括添加)

        save 300 10 #400秒內至少10個key被修改

        save 10000 #60秒內至少有10000個key 被修改.

        這三條配置不沖突, 只要滿足一條就觸發.

        rdb 文件位置和目錄 (默認在安裝根目錄下) 

        #文件路徑

        dir ./

        #文件名稱

        dbfilename mp.rdb

        #是否以LZY壓縮rdb 文件

        rdbcompression yes 

        #開啟數據校驗

        rdbchecksum yes

b) shutdown觸發 ,保證伺服器正常關閉.

c) flushall , rdb文件是空的, 會生成一個空的文件,所以這種情況也沒有什麼意義.但需要知道,這種情況下

會觸發生成rdb文件.

Redis 提供了兩條命令: save 和 bgsave

a). save 命令

        save 在生成快照的時候會阻塞當前Redis 伺服器,Redis不能處理其他命令.如果內存數據較多,會造成

b).bgsave 命令

        執行bgsave命令時,   Redis會在後台進行非同步快照操作,快照同時還可以響應客戶端請求.

具體操作

        具體操作:Redis進程會執行fork操作創建子進程(-on-write),RDB 持久化過程由子進程負載,完成後自動結束.它不會記錄fork之後產生的記錄.阻塞只發送在fork階段,一般時間較短.

一.優勢

    1.RDB是一個非常緊湊的文件,它保存了Redis在某個時間點上的數據集.這種文件非常適合進行備份和

災難恢復.

    2.生成RDB文件的時候,redis主進程會fork()一個子進程來處理所有保存的工作,主進程不需要進行任何

IO操作.

    3.RDB在恢復大數據集時的速度比AOF的恢復速度要快

二.劣勢

    1).RDB 沒辦法做到實時持久化/妙級持久化.因為bgsave每次運行都要執行fork創建子進程,頻繁執行成本過高.    

    2).在一定間隔時間做一次備份,所以如果Redis 以為down掉的話,就會丟失最後一次快照之後所有修改

(數據有丟失)

AOF:<Append Only File> , 記錄日誌

Redis 默認不開啟.AOF採用日誌的形式來記錄每個寫操作,並追加到文件中.開啟後,執行更改Redis    命令時,就會把命令寫入到AOF文件中.

Redis 重啟時會根據日誌文件的內容把寫指令從前往後執行一次以完成數據的恢復工作.

#開關

appendonly no

#文件名

appendfilename "appendonly.aof"

        由於操作系統緩存機制,AOF數據並沒有真正地寫入硬碟,而是進入了系統的硬碟緩存.什麼時候

把緩沖區的內容寫入到AOF文件中? 由下面參數決定

appendfsync :  值: no  \ always \everysec 

        no: 表示不執行fync, 由操作系統保證數據同步到磁碟,速度最快,但是不安全.

        always:表示每次寫入都執行fync,以保證數據同步到磁碟,效率很低

        everysec:表示每秒執行以fync ,可能會導致丟失1s數據.通常選擇everysec,兼顧效率和安全性.

    因為AOF文件只有一個, 隨著redis 不斷進行,AOF 的文件會越來越大,文件越大, 文件佔用伺服器內存

以及AOF恢復要求時間越長.

    為了解決這個問題,可以使用bgwriteaof來重寫.那什麼時候重寫? 又是怎樣重寫?

    一. 什麼時候重寫?

    #重寫觸發機制

    auto-aof-rewrite-percentage 100 默認值是100. 當前aof 文件大小超過 上一次重寫的aof文件大小百分之多少進行重寫,即當aof文件增長到一定大小時,Redis能夠調用bgwriteaof對日誌文件進行重寫.當前aof文件大小是上次日誌重寫得到aof文件大小的二倍時, 自動啟動新的日誌重寫過程.

    auto-aof-rewrite-min-size 默認是64M.設置允許重寫的最小aof文件大小,避免達到了約定百分比 但尺寸

仍然很小的情況還要重寫.

   二. 怎樣重寫?

    並不是對原文件進行重新整理,而是直接讀取伺服器上現有的鍵值對,然後用一條命令去代替之間記錄這

個鍵值對的多條命令,生成一個新的文件後去替換原來的 AOF文件.

    看下面這兩個參數:

        no-appendfsync-on-rewrite

        aof-load-truncated

   AOF 數據恢復

        重啟Redis之後就會進行AOF文件恢復.

   AOF 的優勢和劣勢

優點:

1.AOF 持久化的方法提供了多種的同步頻率,即使使用默認的同步頻率每秒同步一次,Redis最多也就丟失

1秒的數據.

缺點:

1.對於具有相同數據的Redis, AOF文件通常比RDF文件體積更大(RDB存的是數據快照)

2.雖然AOF提供了多種同步的頻率,默認的情況下,沒秒同步一次的頻率也具有較高的性能.在高並發的情況下,RDB比AOF具有更好的性能.

        如果可以忍一小段時間數據的丟失,毫無疑問使用RDB 是最好的,定時生成RDB快照非常便於進行數據備份,而且RDB恢復數據集的速度也要比AOF恢復速度要快.

        否則就要使用AOF重寫.但是一般情況下建議不要單獨使用某一種持久化機制,而是兩種一起用.

本文內容來自咕泡學院-青山老師,感謝青山老師!!

B. Redis的LRU緩存淘汰演算法實現

LRU, 最近最少使用 (Least Recently Used,LRU),經典緩存演算法。

LRU會使用一個鏈表維護緩存中每個數據的訪問情況,並根據數據的實時訪問,調整數據在鏈表中的位置,然後通過數據在鏈表中的位置,表示數據是最近剛訪問的,還是已有段時間未訪問。

LRU會把鏈頭、尾分別設為MRU端和LRU端:

LRU可分成如下情況:

case2圖解:鏈表長度為5,從鏈表頭部到尾部保存的數據分別是5,33,9,10,8。假設數據9被訪問一次,則9就會被移動到鏈表頭部,同時,數據5和33都要向鏈表尾部移動一位。

所以若嚴格按LRU實現,假設Redis保存的數據較多,還要在代碼中實現:

最終導致降低Redis訪問性能。

所以,無論是為節省內存 or 保持Redis高性能,Redis並未嚴格按LRU基本原理實現,而是 提供了一個近似LRU演算法實現

Redis的內存淘汰機制是如何啟用近似LRU演算法的?redis.conf中的如下配置參數:

所以,一旦設定maxmemory選項,且將maxmemory-policy配為allkeys-lru或volatile-lru,近似LRU就被啟用。allkeys-lru和volatile-lru都會使用近似LRU淘汰數據,區別在於:

Redis如何實現近似LRU演算法的呢?

近似LRU演算法仍需區分不同數據的訪問時效性,即Redis需知道數據的最近一次訪問時間。因此,有了LRU時鍾:記錄數據每次訪問的時間戳。

Redis對每個KV對中的V,會使用個redisObject結構體保存指向V的指針。那redisObject除記錄值的指針,還會使用24 bits保存LRU時鍾信息,對應的是lru成員變數。這樣,每個KV對都會把它最近一次被訪問的時間戳,記錄在lru變數。

redisObject定義包含lru成員變數的定義:

每個KV對的LRU時鍾值是如何計算的?Redis Server使用一個實例級別的全局LRU時鍾,每個KV對的LRU time會根據全局LRU時鍾進行設置。

這全局LRU時鍾保存在Redis全局變數server的成員變數 lruclock

當Redis Server啟動後,調用initServerConfig初始化各項參數時,會調用getLRUClock設置lruclock的值:

於是,就得注意, 若一個數據前後兩次訪問的時間間隔 1s,那這兩次訪問的時間戳就是一樣的! 因為LRU時鍾精度就是1s,它無法區分間隔小於1秒的不同時間戳!

getLRUClock函數將獲得的UNIX時間戳,除以LRU_CLOCK_RESOLUTION後,就得到了以LRU時鍾精度來計算的UNIX時間戳,也就是當前的LRU時鍾值。

getLRUClock會把LRU時鍾值和宏定義LRU_CLOCK_MAX(LRU時鍾能表示的最大值)做與運算。

所以默認情況下,全局LRU時鍾值是以1s為精度計算得UNIX時間戳,且是在initServerConfig中進行的初始化。

那Redis Server運行過程中,全局LRU時鍾值是如何更新的?和Redis Server在事件驅動框架中,定期運行的時間事件所對應的serverCron有關。

serverCron作為時間事件的回調函數,本身會周期性執行,其頻率值由redis.conf的 hz配置項 決定,默認值10,即serverCron函數會每100ms(1s/10 = 100ms)運行一次。serverCron中,全局LRU時鍾值就會按該函數執行頻率,定期調用getLRUClock進行更新:

這樣,每個KV對就能從全局LRU時鍾獲取最新訪問時間戳。

對於每個KV對,它對應的redisObject.lru在哪些函數進行初始化和更新的呢?

對於一個KV對,其LRU時鍾值最初是在這KV對被創建時,進行初始化設置的,這初始化操作在 createObject函數 中調用,當Redis要創建一個KV對,就會調用該函數。

createObject除了會給redisObject分配內存空間,還會根據maxmemory_policy配置,初始化設置redisObject.lru。

LRU_CLOCK返回當前全局LRU時鍾值。因為一個KV對一旦被創建,就相當於有了次訪問,其對應LRU時鍾值就表示了它的訪問時間戳:

那一個KV對的LRU時鍾值又是何時再被更新?

只要一個KV對被訪問,其LRU時鍾值就會被更新!而當一個KV對被訪問時,訪問操作最終都會調用 lookupKey

lookupKey會從全局哈希表中查找要訪問的KV對。若該KV對存在,則lookupKey會根據maxmemory_policy的配置值,來更新鍵值對的LRU時鍾值,也就是它的訪問時間戳。

而當maxmemory_policy沒有配置為LFU策略時,lookupKey函數就會調用LRU_CLOCK函數,來獲取當前的全局LRU時鍾值,並將其賦值給鍵值對的redisObject結構體中的lru變數

這樣,每個KV對一旦被訪問,就能獲得最新的訪問時間戳。但你可能好奇:這些訪問時間戳最終是如何被用於近似LRU演算法進行數據淘汰的?

Redis之所以實現近似LRU,是為減少內存資源和操作時間上的開銷。

近似LRU主要邏輯在performEvictions。

performEvictions被evictionTimeProc調用,而evictionTimeProc函數又是被processCommand調用。

processCommand,Redis處理每個命令時都會調用:

然後,isSafeToPerformEvictions還會再次根據如下條件判斷是否繼續執行performEvictions:

一旦performEvictions被調用,且maxmemory-policy被設置為allkeys-lru或volatile-lru,近似LRU就被觸發執行了。

執行可分成如下步驟:

調用getMaxmemoryState評估當前內存使用情況,判斷當前Redis Server使用內存容量是否超過maxmemory配置值。

若未超過maxmemory ,則返回C_OK,performEvictions也會直接返回。

getMaxmemoryState評估當前內存使用情況的時候,若發現已用內存超出maxmemory,會計算需釋放的內存量。這個釋放內存大小=已使用內存量-maxmemory。

但已使用內存量並不包括用於主從復制的復制緩沖區大小,這是getMaxmemoryState通過調用freeMemoryGetNotCountedMemory計算的。

而若當前Server使用的內存量超出maxmemory上限 ,則performEvictions會執行while循環淘汰數據釋放內存。

為淘汰數據,Redis定義數組EvictionPoolLRU,保存待淘汰的候選KV對,元素類型是evictionPoolEntry結構體,保存了待淘汰KV對的空閑時間idle、對應K等信息:

這樣,Redis Server在執行initSever進行初始化時,會調用evictionPoolAlloc為EvictionPoolLRU數組分配內存空間,該數組大小由EVPOOL_SIZE決定,默認可保存16個待淘汰的候選KV對。

performEvictions在淘汰數據的循環流程中,就會更新這個待淘汰的候選KV對集合,即EvictionPoolLRU數組。

performEvictions調用evictionPoolPopulate,其會先調用dictGetSomeKeys,從待采樣哈希表隨機獲取一定數量K:

於是,dictGetSomeKeys返回採樣的KV對集合。evictionPoolPopulate根據實際采樣到的KV對數量count,執行循環:調用estimateObjectIdleTime計算在采樣集合中的每一個KV對的空閑時間:

接著,evictionPoolPopulate遍歷待淘汰的候選KV對集合,即EvictionPoolLRU數組,嘗試把采樣的每個KV對插入EvictionPoolLRU數組,取決於如下條件之一:

有一成立,evictionPoolPopulate就能把采樣KV對插入EvictionPoolLRU數組。等所有采樣鍵值對都處理完後,evictionPoolPopulate函數就完成對待淘汰候選鍵值對集合的更新了。

接下來,performEvictions開始選擇最終被淘汰的KV對。

因evictionPoolPopulate已更新EvictionPoolLRU數組,且該數組里的K,是按空閑時間從小到大排好序了。所以,performEvictions遍歷一次EvictionPoolLRU數組,從數組的最後一個K開始選擇,若選到的K非空,就把它作為最終淘汰的K。

該過程執行邏輯:

一旦選到被淘汰的K,performEvictions就會根據Redis server的惰性刪除配置,執行同步刪除或非同步刪除:

至此,performEvictions就淘汰了一個K。若此時釋放的內存空間還不夠,即沒有達到待釋放空間,則performEvictions還會 重復執行 前面所說的更新待淘汰候選KV對集合、選擇最終淘汰K的過程,直到滿足待釋放空間的大小要求。

performEvictions流程:

近似LRU演算法並未使用耗時且耗空間的鏈表,而使用 固定大小的待淘汰數據集合 ,每次隨機選擇一些K加入待淘汰數據集合。

最後,按待淘汰集合中K的空閑時間長度,刪除空閑時間最長的K。

根據LRU演算法的基本原理,發現若嚴格按基本原理實現LRU演算法,則開發的系統就需要額外內存空間保存LRU鏈表,系統運行時也會受到LRU鏈表操作的開銷影響。

而Redis的內存資源和性能都很重要,所以Redis實現近似LRU演算法:

一個演算法的基本原理和演算法的實際執行,在系統開發中會有一定折中,需綜合考慮所開發的系統,在資源和性能方面的要求,以避免嚴格按照演算法實現帶來的資源和性能開銷。

C. redis 失效如何快速響應

Redis是key-value資料庫,我們可以設置Redis中緩存的key的過期時間。Redis的過期策略就是指當Redis中緩存的key過期了,Redis如何處理。
過期策略通常有以下三種:
1、定時過期:每個設置過期時間的key都需要創建一個定時器,到過期時間就會立即清除。該策略可以立即清除過期的數據,對內存很友好;但是會佔用大量的CPU資源去處理過期的數據,從而影響緩存的響應時間和吞吐量。
2、惰性過期:只有當訪問一個key時,才會判斷該key是否已過期,過期則清除。該策略可以最大化地節省CPU資源,卻對內存非常不友好。極端情況可能出現大量的過期key沒有再次被訪問,從而不會被清除,佔用大量內存。
3、定期過期:每隔一定的時間,會掃描一定數量的資料庫的expires字典中一定數量的key,並清除其中已過期的key。該策略是前兩者的一個折中方案。通過調整定時掃描的時間間隔和每次掃描的限定耗時,可以在不同情況下使得CPU和內存資源達到最優的平衡效果。(expires字典會保存所有設置了過期時間的key的過期時間數據,其中,key是指向鍵空間中的某個鍵的指針,value是該鍵的毫秒精度的UNIX時間戳表示的過期時間。鍵空間是指該Redis集群中保存的所有鍵。)

D. redis過期策略有哪些

redis 過期策略是:定期刪除+惰性刪除。

所謂定期刪除,指的是redis默認是每隔100ms就隨機抽取一些設置了過期時間的key,檢查其是否過期,如果過期就刪除。

假設redis里放了10w個key,都設置了過期時間,每隔幾百毫秒,就檢查10w個key,那redis基本上就死了,cpu負載會很高的,消耗在檢查過期key上了。實際上redis是每隔100ms隨機抽取一些key來檢查和刪除的。

Redis常用內存優化:

Redis內部實現沒有對內存分配方面做過多的優化,在一定程度上會存在內存碎片,不過大多數情況下這個不會成為Redis的性能瓶頸,不過如果在Redis內部存儲的大部分數據是數值型的話,Redis內部採用了一個shared integer的方式來省去分配內存的開銷。

即在系統啟動時先分配一個從1~n那麼多個數值對象放在一個池子中,如果存儲的數據恰好是這個數值范圍內的數據,則直接從池子里取出該對象,並且通過引用計數的方式來共享,這樣在系統存儲了大量數值下。

也能一定程度上節省內存並且提高性能,這個參數值n的設置需要修改源代碼中的一行宏定義REDIS_SHARED_INTEGERS,該值默認是10000,可以根據自己的需要進行修改,修改後重新編譯就可以了。

以上內容參考網路—Redis

E. 該怎麼解決 Redis 緩存穿透和緩存雪崩問題

緩存雪崩: 由於緩存層承載著大量請求,有效地 保護了存儲層,但是如果緩存層由於某些原因不能提供服務,比如 Redis 節點掛掉了,熱點 key 全部失效了,在這些情況下,所有的請求都會直接請求到資料庫,可能會造成資料庫宕機的情況。
預防和解決緩存雪崩問題,可以從以下三個方面進行著手:
1、使用 Redis 高可用架構:使用 Redis 集群來保證 Redis 服務不會掛掉
2、緩存時間不一致: 給緩存的失效時間,加上一個隨機值,避免集體失效
3、限流降級策略:有一定的備案,比如個性推薦服務不可用了,換成熱點數據推薦服務
緩存穿透: 緩存穿透是指查詢一個根本不存在的數據,這樣的數據肯定不在緩存中,這會導致請求全部落到資料庫上,有可能出現資料庫宕機的情況。
預防和解決緩存穿透問題,可以考慮以下兩種方法:
1、緩存空對象: 將空值緩存起來,但是這樣就有一個問題,大量無效的空值將佔用空間,非常浪費。
2、布隆過濾器攔截: 將所有可能的查詢key 先映射到布隆過濾器中,查詢時先判斷key是否存在布隆過濾器中,存在才繼續向下執行,如果不存在,則直接返回。布隆過濾器有一定的誤判,所以需要你的業務允許一定的容錯性。

F. 2022-03-12 SpringBoot 使用redis做緩存,設置失效時間以及序列化

SpringBoot的cache緩存,是針對返回值的一種操作
springboot使用redis做緩存時,默認只需要導入redis依賴,即可實現使用redis做緩存

不需要導入cache依賴
在啟動類上加上 @EnableCaching 即可開啟緩存功能
關於各個註解的使用,這里不再細說,網上詳細的教程很多,這里主要講一下如何指定過期時間

默認是永不過期,如果需要指定過期時間,則需要增加配置類

G. Redis常見延遲問題排查手冊!附33條優化建議

Redis作為內存資料庫,擁有非常高的性能,單個實例的QPS能夠達到10W左右。但我們在使用Redis時,經常時不時會出現訪問延遲很大的情況,如果你不知道Redis的內部實現原理,在排查問題時就會一頭霧水。

很多時候,Redis出現訪問延遲變大,都與我們的使用不當或運維不合理導致的。

下面我們就來分析一下Redis在使用過程中,經常會遇到的延遲問題以及如何定位和分析。

如果在使用Redis時,發現訪問延遲突然增大,如何進行排查?

首先,第一步,建議你去查看一下Redis的慢日誌。Redis提供了慢日誌命令的統計功能,我們通過以下設置,就可以查看有哪些命令在執行時延遲比較大。

首先設置Redis的慢日誌閾值,只有超過閾值的命令才會被記錄,這里的單位是微妙,例如設置慢日誌的閾值為5毫秒,同時設置只保留最近1000條慢日誌記錄:

# 命令執行超過5毫秒記錄慢日誌

CONFIG SET slowlog-log-slower-than 5000

# 只保留最近1000條慢日誌

CONFIG SET slowlog-max-len 1000

設置完成之後,所有執行的命令如果延遲大於5毫秒,都會被Redis記錄下來,我們執行SLOWLOG get 5查詢最近5條慢日誌:

127.0.0.1:6379> SLOWLOG get 5

1) 1) (integer) 32693 # 慢日誌ID

2) (integer) 1593763337 # 執行時間

3) (integer) 5299 # 執行耗時(微妙)

4) 1) 'LRANGE' # 具體執行的命令和參數

2) 'user_list_2000'

3) Ɔ'

4) '-1'

2) 1) (integer) 32692

2) (integer) 1593763337

3) (integer) 5044

4) 1) 'GET'

2) 'book_price_1000'

...

通過查看慢日誌記錄,我們就可以知道在什麼時間執行哪些命令比較耗時, 如果你的業務經常使用O(n)以上復雜度的命令, 例如sort、sunion、zunionstore,或者在執行O(n)命令時操作的數據量比較大,這些情況下Redis處理數據時就會很耗時。

如果你的服務請求量並不大,但Redis實例的CPU使用率很高,很有可能是使用了復雜度高的命令導致的。

解決方案就是,不使用這些復雜度較高的命令,並且一次不要獲取太多的數據,每次盡量操作少量的數據,讓Redis可以及時處理返回。

如果查詢慢日誌發現,並不是復雜度較高的命令導致的,例如都是SET、DELETE操作出現在慢日誌記錄中,那麼你就要懷疑是否存在Redis寫入了大key的情況。

Redis在寫入數據時,需要為新的數據分配內存,當從Redis中刪除數據時,它會釋放對應的內存空間。

如果一個key寫入的數據非常大,Redis 在分配內存時也會比較耗時。 同樣的,當刪除這個key的數據時, 釋放內存也會耗時比較久。

你需要檢查你的業務代碼,是否存在寫入大key的情況,需要評估寫入數據量的大小,業務層應該避免一個key存入過大的數據量。

那麼有沒有什麼辦法可以掃描現在Redis中是否存在大key的數據嗎?

Redis也提供了掃描大key的方法:

redis-cli -h $host -p $port --bigkeys -i 0.01

使用上面的命令就可以掃描出整個實例key大小的分布情況,它是以類型維度來展示的。

需要注意的是當我們在線上實例進行大key掃描時,Redis的QPS會突增,為了降低掃描過程中對Redis的影響,我們需要控制掃描的頻率,使用-i參數控制即可,它表示掃描過程中每次掃描的時間間隔,單位是秒。

使用這個命令的原理,其實就是Redis在內部執行scan命令,遍歷所有key,然後針對不同類型的key執行strlen、llen、hlen、scard、zcard來獲取字元串的長度以及容器類型(list/dict/set/zset)的元素個數。

而對於容器類型的key,只能掃描出元素最多的key,但元素最多的key不一定佔用內存最多,這一點需要我們注意下。不過使用這個命令一般我們是可以對整個實例中key的分布情況有比較清晰的了解。

針對大key的問題,Redis官方在4.0版本推出了lazy-free的機制,用於非同步釋放大key的內存,降低對Redis性能的影響。即使這樣,我們也不建議使用大key,大key在集群的遷移過程中,也會影響到遷移的性能,這個後面在介紹集群相關的文章時,會再詳細介紹到。

有時你會發現,平時在使用Redis時沒有延時比較大的情況,但在某個時間點突然出現一波延時,而且 報慢的時間點很有規律,例如某個整點,或者間隔多久就會發生一次。

如果出現這種情況,就需要考慮是否存在大量key集中過期的情況。

如果有大量的key在某個固定時間點集中過期,在這個時間點訪問Redis時,就有可能導致延遲增加。

Redis的過期策略採用主動過期+懶惰過期兩種策略:

注意, Redis的主動過期的定時任務,也是在Redis主線程中執行的 ,也就是說如果在執行主動過期的過程中,出現了需要大量刪除過期key的情況,那麼在業務訪問時,必須等這個過期任務執行結束,才可以處理業務請求。此時就會出現,業務訪問延時增大的問題,最大延遲為25毫秒。

而且這個訪問延遲的情況, 不會記錄在慢日誌里。 慢日誌中 只記錄真正執行某個命令的耗時 ,Redis主動過期策略執行在操作命令之前,如果操作命令耗時達不到慢日誌閾值,它是不會計算在慢日誌統計中的,但我們的業務卻感到了延遲增大。

此時你需要檢查你的業務,是否真的存在集中過期的代碼,一般集中過期使用的命令是expireat或pexpireat命令,在代碼中搜索這個關鍵字就可以了。

如果你的業務確實需要集中過期掉某些key,又不想導致Redis發生抖動,有什麼優化方案?

解決方案是, 在集中過期時增加一個隨機時間,把這些需要過期的key的時間打散即可。

偽代碼可以這么寫:

# 在過期時間點之後的5分鍾內隨機過期掉

redis.expireat(key, expire_time + random(300))

這樣Redis在處理過期時,不會因為集中刪除key導致壓力過大,阻塞主線程。

另外,除了業務使用需要注意此問題之外,還可以通過運維手段來及時發現這種情況。

我們需要對這個指標監控,當在 很短時間內這個指標出現突增 時,需要及時報警出來,然後與業務報慢的時間點對比分析,確認時間是否一致,如果一致,則可以認為確實是因為這個原因導致的延遲增大。

有時我們把Redis當做純緩存使用,就會給實例設置一個內存上限maxmemory,然後開啟LRU淘汰策略。

當實例的內存達到了maxmemory後,你會發現之後的每次寫入新的數據,有可能變慢了。

導致變慢的原因是,當Redis內存達到maxmemory後,每次寫入新的數據之前,必須先踢出一部分數據,讓內存維持在maxmemory之下。

這個踢出舊數據的邏輯也是需要消耗時間的,而具體耗時的長短,要取決於配置的淘汰策略:

具體使用哪種策略,需要根據業務場景來決定。

我們最常使用的一般是allkeys-lru或volatile-lru策略,它們的處理邏輯是,每次從實例中隨機取出一批key(可配置),然後淘汰一個最少訪問的key,之後把剩下的key暫存到一個池子中,繼續隨機取出一批key,並與之前池子中的key比較,再淘汰一個最少訪問的key。以此循環,直到內存降到maxmemory之下。

如果使用的是allkeys-random或volatile-random策略,那麼就會快很多,因為是隨機淘汰,那麼就少了比較key訪問頻率時間的消耗了,隨機拿出一批key後直接淘汰即可,因此這個策略要比上面的LRU策略執行快一些。

但以上這些邏輯都是在訪問Redis時,真正命令執行之前執行的,也就是它會影響我們訪問Redis時執行的命令。

另外,如果此時Redis實例中有存儲大key,那麼在淘汰大key釋放內存時,這個耗時會更加久,延遲更大,這需要我們格外注意。

如果你的業務訪問量非常大,並且必須設置maxmemory限制實例的內存上限,同時面臨淘汰key導致延遲增大的的情況,要想緩解這種情況,除了上面說的避免存儲大key、使用隨機淘汰策略之外,也可以考慮拆分實例的方法來緩解,拆分實例可以把一個實例淘汰key的壓力分攤到多個實例上,可以在一定程度降低延遲。

如果你的Redis開啟了自動生成RDB和AOF重寫功能,那麼有可能在後台生成RDB和AOF重寫時導致Redis的訪問延遲增大,而等這些任務執行完畢後,延遲情況消失。

遇到這種情況,一般就是執行生成RDB和AOF重寫任務導致的。

生成RDB和AOF都需要父進程fork出一個子進程進行數據的持久化,在fork執行過程中,父進程需要拷貝內存頁表給子進程,如果整個實例內存佔用很大,那麼需要拷貝的內存頁表會比較耗時,此過程會消耗大量的CPU資源,在完成fork之前,整個實例會被阻塞住,無法處理任何請求,如果此時CPU資源緊張,那麼fork的時間會更長,甚至達到秒級。這會嚴重影響Redis的性能。

具體原理也可以參考我之前寫的文章:Redis持久化是如何做的?RDB和AOF對比分析。

我們可以執行info命令,查看最後一次fork執行的耗時latest_fork_usec,單位微妙。這個時間就是整個實例阻塞無法處理請求的時間。

除了因為備份的原因生成RDB之外,在 主從節點第一次建立數據同步時 ,主節點也會生成RDB文件給從節點進行一次全量同步,這時也會對Redis產生性能影響。

要想避免這種情況,我們需要規劃好數據備份的周期,建議 在從節點上執行備份,而且最好放在低峰期執行。 如果對於丟失數據不敏感的業務,那麼不建議開啟AOF和AOF重寫功能。

另外,fork的耗時也與系統有關,如果把Redis部署在虛擬機上,那麼這個時間也會增大。所以使用Redis時建議部署在物理機上,降低fork的影響。

很多時候,我們在部署服務時,為了提高性能,降低程序在使用多個CPU時上下文切換的性能損耗,一般會採用進程綁定CPU的操作。

但在使用Redis時,我們不建議這么干,原因如下。

綁定CPU的Redis,在進行數據持久化時,fork出的子進程,子進程會繼承父進程的CPU使用偏好,而此時子進程會消耗大量的CPU資源進行數據持久化,子進程會與主進程發生CPU爭搶,這也會導致主進程的CPU資源不足訪問延遲增大。

所以在部署Redis進程時,如果需要開啟RDB和AOF重寫機制,一定不能進行CPU綁定操作!

上面提到了,當執行AOF文件重寫時會因為fork執行耗時導致Redis延遲增大,除了這個之外,如果開啟AOF機制,設置的策略不合理,也會導致性能問題。

開啟AOF後,Redis會把寫入的命令實時寫入到文件中,但寫入文件的過程是先寫入內存,等內存中的數據超過一定閾值或達到一定時間後,內存中的內容才會被真正寫入到磁碟中。

AOF為了保證文件寫入磁碟的安全性,提供了3種刷盤機制:

當使用第一種機制appendfsync always時,Redis每處理一次寫命令,都會把這個命令寫入磁碟,而且 這個操作是在主線程中執行的。

內存中的的數據寫入磁碟,這個會加重磁碟的IO負擔,操作磁碟成本要比操作內存的代價大得多。如果寫入量很大,那麼每次更新都會寫入磁碟,此時機器的磁碟IO就會非常高,拖慢Redis的性能,因此我們不建議使用這種機制。

與第一種機制對比,appendfsync everysec會每隔1秒刷盤,而appendfsync no取決於操作系統的刷盤時間,安全性不高。因此我們推薦使用appendfsync everysec這種方式,在最壞的情況下,只會丟失1秒的數據,但它能保持較好的訪問性能。

當然,對於有些業務場景,對丟失數據並不敏感,也可以不開啟AOF。

如果你發現Redis突然變得非常慢, 每次訪問的耗時都達到了幾百毫秒甚至秒級 ,那此時就檢查Redis是否使用到了Swap,這種情況下Redis基本上已經無法提供高性能的服務。

我們知道,操作系統提供了Swap機制,目的是為了當內存不足時,可以把一部分內存中的數據換到磁碟上,以達到對內存使用的緩沖。

但當內存中的數據被換到磁碟上後,訪問這些數據就需要從磁碟中讀取,這個速度要比內存慢太多!

尤其是針對Redis這種高性能的內存資料庫來說,如果Redis中的內存被換到磁碟上,對於Redis這種性能極其敏感的資料庫,這個操作時間是無法接受的。

我們需要檢查機器的內存使用情況,確認是否確實是因為內存不足導致使用到了Swap。

如果確實使用到了Swap,要及時整理內存空間,釋放出足夠的內存供Redis使用,然後釋放Redis的Swap,讓Redis重新使用內存。

釋放Redis的Swap過程通常要重啟實例,為了避免重啟實例對業務的影響,一般先進行主從切換,然後釋放舊主節點的Swap,重新啟動服務,待數據同步完成後,再切換回主節點即可。

可見,當Redis使用到Swap後,此時的Redis的高性能基本被廢掉,所以我們需要提前預防這種情況。

我們需要對Redis機器的內存和Swap使用情況進行監控,在內存不足和使用到Swap時及時報警出來,及時進行相應的處理。

如果以上產生性能問題的場景,你都規避掉了,而且Redis也穩定運行了很長時間,但在某個時間點之後開始,訪問Redis開始變慢了,而且一直持續到現在,這種情況是什麼原因導致的?

之前我們就遇到這種問題, 特點就是從某個時間點之後就開始變慢,並且一直持續。 這時你需要檢查一下機器的網卡流量,是否存在網卡流量被跑滿的情況。

網卡負載過高,在網路層和TCP層就會出現數據發送延遲、數據丟包等情況。Redis的高性能除了內存之外,就在於網路IO,請求量突增會導致網卡負載變高。

如果出現這種情況,你需要排查這個機器上的哪個Redis實例的流量過大占滿了網路帶寬,然後確認流量突增是否屬於業務正常情況,如果屬於那就需要及時擴容或遷移實例,避免這個機器的其他實例受到影響。

運維層面,我們需要對機器的各項指標增加監控,包括網路流量,在達到閾值時提前報警,及時與業務確認並擴容。

以上我們總結了Redis中常見的可能導致延遲增大甚至阻塞的場景,這其中既涉及到了業務的使用問題,也涉及到Redis的運維問題。

可見,要想保證Redis高性能的運行,其中涉及到CPU、內存、網路,甚至磁碟的方方面面,其中還包括操作系統的相關特性的使用。

作為開發人員,我們需要了解Redis的運行機制,例如各個命令的執行時間復雜度、數據過期策略、數據淘汰策略等,使用合理的命令,並結合業務場景進行優化。

作為DBA運維人員,需要了解數據持久化、操作系統fork原理、Swap機制等,並對Redis的容量進行合理規劃,預留足夠的機器資源,對機器做好完善的監控,才能保證Redis的穩定運行。

在上文中,主要講解了 Redis 常見的導致變慢的場景以及問題定位和分析,主要是由業務使用不合理和運維不當導致的。

H. Redis緩存過期機制

一、針對與設置了過期時間的key值

    1.(主動)定期刪除:定時隨機的檢查過期的key,如果過期則清理刪除

        redis.conf(每秒檢查的次數1-500)配置:   hz 10

    2.(被動)惰性刪除:當客戶端請求到一個已經過期的key時,redis會檢查是否過期並刪除

所以,雖然key過期了,但是沒被清理的話,還是會占內存的。

二、內存淘汰管理機制Memory Management

    當內存占滿之後,redis提供緩存淘汰機制。

    redis.conf: maxmemory <bytes>

* noeviction:舊緩存永不過期,新緩存設置不了,返回錯誤 

* allkeys-lru:清除最少用的舊緩存,然後保存新的緩存(推薦使用)

* allkeys-random:在所有的緩存中隨機刪除(不推薦)

* volatile-lru:在那些設置了expire過期時間的緩存中,清除最少用的舊緩存,然後保存新的緩存

* volatile-random:在那些設置了expire過期時間的緩存中,隨機刪除緩存

* volatile-ttl:在那些設置了expire過期時間的緩存中,刪除即將過期的

I. redis做mysql的緩存

redis緩存其實就是把經常訪問的數據放到redis裡面,用戶查詢的時候先去redis查詢,沒有查到就執行sql語句查詢,同時把數據同步到redis裡面。redis只做讀操作,在內存中查詢速度快。

使用redis做緩存必須解決兩個問題,首先就是確定用何種數據結構存儲來自mysql的數據;確定數據結構之後就是需要確定用什麼標識來作為數據的key。
mysql是按照表存儲數據的,這些表是由若干行組成。每一次執行select查詢,mysql都會返回一個結果集,這個結果是由若干行組成的。redis有五種數據結構:列表list,哈希hash,字元串string,集合set,sorted set(有序集合),對比幾種數據結構,string和hash是比較適合存儲行的數據結構,可以把數據轉成json字元串存入redis。

全量遍歷鍵: keys pattern keys *
有人說 KEYS 相當於關系性數據的庫的 select * ,在生產環境幾乎是要禁用的

不管上面說的對不對, keys 肯定是有風險的。那我們就換一種方案,在存數據的時候。把數據的鍵存一下,也存到redis裡面選hash類型,那麼取的時候就可以直接通過這個hash獲取所有的值,自我感覺非常好用!

J. 我配置了redis註解緩存,為什麼不起作用

作為緩存伺服器,如果不加以限制內存的話,就很有可能出現將整台伺服器內存都耗光的情況,可以在redis的配置文件裡面設置:
example:
#
限定最多使用1.5GB內存
maxmemory
1536mb
如果內存到達了指定的上限,還要往redis裡面添加更多的緩存內容,需要設置清理內容的策略:
默認為0,沒有指定最大緩存,如果有新的數據添加,超過最大內存,則會使redis崩潰,所以一點要設置。
設置maxmemory之後,配合的要設置緩存數據回收策略。

熱點內容
python函數圖像 發布:2024-04-25 10:17:29 瀏覽:913
提供華為雲雲資料庫 發布:2024-04-25 10:12:32 瀏覽:591
演算法設計手冊 發布:2024-04-25 10:03:24 瀏覽:80
linuxapache緩存 發布:2024-04-25 10:03:12 瀏覽:109
內務櫃密碼鎖去哪裡買 發布:2024-04-25 10:03:07 瀏覽:820
androidtimestamp 發布:2024-04-25 09:06:07 瀏覽:608
玩火影筆記本要什麼配置 發布:2024-04-25 08:34:59 瀏覽:209
sql性能監視器 發布:2024-04-25 08:21:48 瀏覽:832
吃雞ak配置什麼最好 發布:2024-04-25 08:15:46 瀏覽:447
firefox緩存目錄 發布:2024-04-25 08:00:31 瀏覽:940