由於Redis是基於記憶體的操作,因此CPU並非其效能瓶頸。相反,伺服器的記憶體利用率、網路IO和磁碟讀寫對Redis的效能起著關鍵作用。
因此,我們將專注於從網路、記憶體、磁碟和阻塞點等方面展開最佳化。如有術語不清楚,建議參考前幾期的redis內容或查閱相關資料。
網路最佳化
如果是客戶端請求服務端,也就是「請求-回應」模式下,盡可能的使用批次處理來減少網路IO的開銷。
批次處理的技術:原子性m批次指令、pipline技術、redis、事務,lua腳本。
批次減少網路IO開銷
原子性m批次處理指令:string類型,建議使用mget/mset取代get/set;hash類型,建議使用hmget/hmset取代hget/hset。
pipline技術:使用list、set和zset時有批次操作時可以使用pipeline技術。
redis事務:在特殊業務要求保證多個指令時建議使用。
lua腳本:在需要保證多條指令原子性時建議使用lua腳本,具體案例有分散式的解鎖、秒殺和減庫存。
節點間網路最佳化
在同一個區域網路中搭建叢集;
控制切分群集的節點數,redis實例上分配的雜湊槽需要在不同實例之間傳遞,以及負債均衡實例刪除時,資料會在不同實例之間傳遞。但哈希槽資訊不大,而資料遷移是漸進式的,但不是主要的問題;
記憶體最佳化
控制key的長度:建議開發前先定義好規範,保證key在簡單、清晰的前提下,盡可能把key按業務縮寫。
杜絕bigkey:string類型的建議大小在20KB以內。 hash、list、set和zset建議控制好閾值,建議控制在5000以內。
key設定過期內:充分利用記憶體。
選擇合適的資料結構
string類型,建議使用整數型,它底層編碼會選擇整數型編碼,記憶體開銷小;
hash類型,建議控制元素閾值,元素少時底層會用壓縮列表資料結構,記憶體開銷小;list類型,建議控制元素閾值,元素少時底層會用壓縮列表資料結構,記憶體開銷小;set類型,建議儲存整數型,它底層編碼會選擇整數型編碼,記憶體開銷小;
zset類型,建議控制元素閾值,元素少時底層會用壓縮列表資料結構,記憶體開銷小;
資料壓縮:客戶端在寫入redis前可以採用snappy、gzip等壓縮演算法對資料壓縮,減少記憶體佔用,但客戶端在讀取資料後需要對資料做解壓,會消耗更多的CPU。
開啟記憶體淘汰策略
杜絕預設記憶體淘汰策略,請結合實際業務選擇合適的淘汰策略,提高記憶體的使用率。
LRU:專注於造訪次數,淘汰最近最少使用的key,使用場景比較廣。 redis的LRU採用近似LRU的演算法,為key增加一個額外欄位長度為24bit,為最後一次造訪的時間戳記。採取懶惰方式處理:當執行寫入操作時如果超出最大內存就執行一次LRU淘汰算法,隨機採樣5(數量可設置)個key,淘汰掉最舊的key,如果淘汰後依舊超出最大內存則繼續淘汰。
LFU:專注於存取頻率,在處理快取污染時,建議使用。
記憶體碎片優化問題
原因:一個是記憶體分配器的分配策略造成的,記憶體分配器是按照固定大小分配的,而不是按照實際申請的大小分配的,如申請位元組以內,實際分配32位元組;另一個是redis鍵值對刪除之後會釋放部分空間帶來的記憶體碎片。
定位:透過指令INFO memory來觀察men_fragmentation_ratio的指標;指標在1-1.5之間,則屬於正常;指標大於1.5,則記憶體碎片率已經超過了50%,需要處理記憶體碎片了;
方案:重啟redis實例;
開啟redis自動記憶體碎片清理功能。
磁碟最佳化
實體搭建redis服務:持久化時,redis採用創建子程序的方式進行(會呼叫作業系統的fork系統),而虛擬機器環境執行fork的耗時,要比實體機慢。
持久化最佳化機制
不開啟持久化:redis只做快取使用,則不需要開啟持久化,減少磁碟的開銷;
AOF優化:後台處理AOF,設定appenfsync everyec把資料持久化的刷盤操作,放到後台執行緒去執行,盡量降低redis寫磁碟對效能的影響;
不建高頻率的做AOF持久化,AOF持久化預設頻率是每秒1次,不建議修改這個配置,它已經能保證最多遺失1s資料了;
開啟混合持久化,redis4.0支援混合持久化 RDB 增量的AOF;
開啟多執行緒配置,在redis6.0之前,持久化都是透過主執行緒fork子程序處理持久化,但是fork都是同步阻塞,6.0之後支援多進程來處理持久化運算;
叢集優化
slave做持久優化:master不做持久化,盡可能分攤master磁碟IO的壓力;主從優化:增量模式,主從同步方式指定為增量模式,不會選擇全量的RDB模式,全模式是一個非常消耗性能;採用級聯同步模式,一主多從時,多個salve都來master這裡同步數據,會直接拖跨master性能。
對於這個問題,redis支援級聯同步的方式,也就是master只將資料同步給一個salve,然後其他的salve的資料都從這個salve同步,來緩解master壓力。
實際大小建議不超過6G。實例太大會在主從同步會有卡頓現象,嚴重時會拖垮master。
在異常重啟時會重播AOF,如果實例過大資料恢復會異常的緩慢。
阻塞點最佳化
分析:由於Redis處理請求和指令時是單線程,則它的效能瓶頸點是同步阻塞問題。
bigkey問題
危害:讀寫bigkey可能會導致逾時,而redis是單執行緒操作數據,嚴重的會導致阻塞整個redis服務。而且一個key只會分割到一個節點,無法分攤速寫壓力。 bigkey偵測:自帶指令bredis-cli-bigkeys。 redis自備指令,只能找出五種資料型別裡最大的key,並沒有太大作用,不是很推薦。 python掃描腳本。可以定位到具體key,但準確度不高,不建議。
rdb_bigkeys工具。 go寫的⼀款⼯具,時間執⾏快且準確度⾼,還可以直接匯出到csv⽂件,⽅便查看,推薦。
優化:對於⾮string類型bigkey,可以對元素集合進⾏分割,拆分成多個,如將⼀個bigkey拆分成1000個key,則key的後綴使⽤hash取模1000。
使用本地緩存,如redis裡存放業務id 版本號,將具體內容放在本地緩存,每次查詢先查redis緩存,再同本地緩存核對版本號。
優化bigkey一般都是傷痕谷,推薦開發時就定義好規範,避免bigkey問題。
過期策略
定時刪除:每過期key都給一個定時job,到期了就直接刪除,記憶體利用率高,cpu佔用高。惰性刪除:當查詢到key時,才判斷key是否過期,如果過期則刪除,cpu佔用低,記憶體利用率。定期刪除:每隔一段時間掃描一次,過期key直接刪除,cpu和我記憶體利用率一般。
1.貪心策略。 redis會將設定為過期key,單獨放到一個字典中。
2.掃描過程。從過期字典中挑選出20個key,刪除20個key中已過期的key。如果刪除的key佔比超過了1/4則重複步驟1。
基於上述邏輯為了解決循環過度導致執行緒卡死的現象,在演算法上加上超時時間的機制,預設時間是25ms。
3.掃描頻率:redis預設是每秒10次過期掃描。
redis預設是開啟惰性刪除 定期刪除。
優化:開啟lazy-free,釋放記憶體的耗時操作,將會放到後台執行緒中去執行,redis4.0 支援。
開啟多執行緒模式,在redis6.0之前過期策略都是主執行緒的同步操作,6.0之後再採用多執行緒去處理。
複雜度高指令
建議使用scan分批次查詢,不要使用keys。不適用聚合操作:redis是單執行緒模型處理請求,執行複雜度過高的指令(消耗更多CPU資源)時後面的請求會排隊導致延遲,如SINTER、SINTERSTORW、ZUNIONSTORE、ZINTERSTORE等。建議使用scan分批次查出集合中元素,在客戶端做聚合計算。
容器類別資料操作:當容器類別元素非常多時,直接查詢會存在由於網路為你導致的延遲,推薦分批次查詢。
當容器類別元素非常多時,直接刪除key時有可能導致redis卡頓,建議分批次刪除。
以上是redis最佳化指南:網路、記憶體、磁碟,阻塞點的詳細內容。更多資訊請關注PHP中文網其他相關文章!