前言
Redis
作爲高性能的內存數據庫,在大數據量的情況下也會遇到性能瓶頸,日常開發中只有時刻謹記優化鐵則,才能使得Redis性能發揮到極致。
本文將會介紹十三條性能優化軍規,開發過程中只要按照執行,性能必能質的飛躍。
1. 避免慢查詢命令
慢查詢命令指的是執行較慢的命令,Redis自身提供了許多的命令,並不是所有的命令都慢,這和命令的操作複雜度有關,因此必須知道Redis不同命令的複雜度。
如說,Value
類型爲 String
時,GET/SET
操作主要就是操作 Redis 的哈希表索引。這個操作複雜度基本是固定的,即 O(1)
。但是,當 Value
類型爲 Set
時,SORT
、SUNION/SMEMBERS
操作複雜度分別爲 O(N+M*log(M))
和 O(N)
。其中,N
爲 Set
中的元素個數,M
爲 SORT
操作返回的元素個數。這個複雜度就增加了很多。Redis 官方文檔中對每個命令的複雜度都有介紹,當你需要了解某個命令的複雜度時,可以直接查詢。
當你發現 Redis 性能變慢時,可以通過 Redis 日誌,或者是 latency monitor
工具,查詢變慢的請求,根據請求對應的具體命令以及官方文檔,確認下是否採用了複雜度高的慢查詢命令。
如果確實存在大量的慢查詢命令,建議如下兩種方式:
-
用其他高效的命令代替:比如說,如果你需要返回一個 SET
中的所有成員時,不要使用SMEMBERS
命令,而是要使用SSCAN
多次迭代返回,避免一次返回大量數據,造成線程阻塞。 -
當你需要執行排序、交集、並集操作時,可以在客戶端完成,而不要用 SORT
、SUNION、SINTER
這些命令,以免拖慢 Redis 實例。
2. 生產環境禁用keys命令
keys
這個命令是最容易忽略的慢查詢命令,因爲keys命令需要遍歷存儲的鍵值對,所以操作延時很高,在生產環境使用很可能導致Redis阻塞;因此不建議在生產環境中使用keys
命令。
3. keys需要設置過期時間
4. 禁止批量的給keys設置相同的過期時間
100
毫秒會刪除一些過期
key
,具體的算法如下:
-
採樣 ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP
個數的 key,並將其中過期的 key 全部刪除; -
如果超過 25%
的key
過期了,則重複刪除的過程,直到過期key
的比例降至 `25%`` 以下。
ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP
是 Redis 的一個參數,默認是
20
,那麼,一秒內基本有
200
個過期
key
會被刪除。這一策略對清除過期 key、釋放內存空間很有幫助。如果每秒鐘刪除 200 個過期 key,並不會對 Redis 造成太大影響。
Redis 4.0
後可以用異步線程機制來減少阻塞影響)。所以,一旦該條件觸發,Redis 的線程就會一直執行刪除,這樣一來,就沒辦法正常服務其他的鍵值操作了,就會進一步引起其他鍵值操作的延遲增加,Redis 就會變慢。
keys
設置過期時間。
5. 謹慎選擇數據結構
string
、
hash
、
list
、
set
、
zset
(sorted set)。可以發現,大多數場景下使用 string 都可以去解決問題。但是,這並不一定是最優的選擇。下面,簡單說明下它們各自的適用場景:
-
string
:單個的緩存結果,不與其他的 KV 之間有聯繫 -
hash
:一個 Object 包含有很多屬性,且這些屬性都需要單獨存儲。注意:這種情況不要使用 string,因爲 string 會佔據更多的內存 -
list
:一個 Object 包含很多數據,且這些數據允許重複、要求有順序性 -
set
:一個 Object 包含很多數據,不要求數據有順序,但是不允許重複 -
zset
:一個 Object 包含很多數據,且這些數據自身還包含一個權重值,可以利用這個權重值來排序
-
HyperLogLog
:適合用於基數
統計,比如PV,UV的統計,存在誤差
問題,不適合精確統計。 -
BitMap
:適合二值狀態
的統計,比如簽到打卡,要麼打卡了,要麼未打卡。
6. 檢查持久化策略
-
AOF日誌
:一種採用文件追加的方式將命令記錄在日誌中的策略,針對同步和異步追加還提供了三個配置項,有興趣的可以查看官方文檔。 -
RDB快照
:以快照的方式,將某一個時刻的內存數據,以二進制的方式寫入磁盤。 -
AOF
和RDB
混用:Redis4.0新增的方式,爲了採用兩種方式各自的優點,在RDB快照的時間段內使用的AOF日誌記錄這段時間的操作的命令,這樣一旦發生宕機,將不會丟失兩段快照中間的數據。
7. 採用高速的固態硬盤作爲日誌寫入設備
8. 使用物理機而非虛擬機
基線性能
:
./redis-cli --intrinsic-latency 120
9. 增加機器內存或者使用Redis集羣
Swap
。
swap
是操作系統裏將內存數據在內存和磁盤間來回換入和換出的機制,涉及到磁盤的讀寫,所以,一旦觸發
swap
,無論是被換入數據的進程,還是被換出數據的進程,其性能都會受到慢速磁盤讀寫的影響。
swap
的影響,而導致性能變慢。
swap
被觸發了,Redis 的請求操作需要等到磁盤數據讀寫完成纔行。而且,和我剛纔說的
AOF
日誌文件讀寫使用
fsync
線程不同,
swap
觸發後影響的是 Redis 主
IO
線程,這會極大地增加 Redis 的響應時間。
Swap
,提高性能。
10. 使用 Pipeline 批量操作數據
Pipeline
(管道技術) 是客戶端提供的一種批處理技術,用於一次處理多個 Redis 命令,從而提高整個交互的性能。
11. 客戶端使用優化
Pipeline
的技術外,還需要注意要儘量使用 Redis 連接池,而不是頻繁創建銷燬 Redis 連接,這樣就可以減少網絡傳輸次數和減少了非必要調用指令。
12. 使用分佈式架構來增加讀寫速度
-
主從同步 -
哨兵模式 -
Redis Cluster
集羣
Redis Cluster
是
Redis 3.0
正式推出的,Redis 集羣是通過將數據分散存儲到多個節點上,來平衡各個節點的負載壓力。
Redis Cluster
採用虛擬哈希槽分區,所有的鍵根據哈希函數映射到
0 ~ 16383
整數槽內,計算公式:
slot = CRC16(key) & 16383
,每一個節點負責維護一部分槽以及槽所映射的鍵值數據。這樣 Redis 就可以把讀寫壓力從一臺服務器,分散給多臺服務器了,因此性能會有很大的提升。
Redis Cluster
應該是首選的實現方案,它可以把讀寫壓力自動的分擔給更多的服務器,並且擁有自動容災的能力。
13. 避免內存碎片
INFO memory
可以查看內存的使用信息,如下:
INFO memory
# Memory
used_memory:1073741736
used_memory_human:1024.00M
used_memory_rss:1997159792
used_memory_rss_human:1.86G
…
mem_fragmentation_ratio:1.86
mem_fragmentation_ratio
的指標,它表示的就是 Redis 當前的內存碎片率。那麼,這個碎片率是怎麼計算的呢?其實,就是上面的命令中的兩個指標
used_memory_rss
和
used_memory
相除的結果。
mem_fragmentation_ratio = used_memory_rss/ used_memory
used_memory_rss
是操作系統實際分配給 Redis 的物理內存空間,裏面就包含了碎片;而
used_memory
是 Redis 爲了保存數據實際申請使用的空間。
-
mem_fragmentation_ratio
大於 1 但小於 1.5。這種情況是合理的。這是因爲,剛纔我介紹的那些因素是難以避免的。畢竟,內因的內存分配器是一定要使用的,分配策略都是通用的,不會輕易修改;而外因由 Redis 負載決定,也無法限制。所以,存在內存碎片也是正常的。 -
mem_fragmentation_ratio
大於 1.5 。這表明內存碎片率已經超過了 50%。一般情況下,這個時候,我們就需要採取一些措施來降低內存碎片率了。
有道無術,術可成;有術無道,止於術
歡迎大家關注Java之道公衆號
好文章,我在看❤️
本文分享自微信公衆號 - Hollis(hollischuang)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。