網(wǎng)站首頁 編程語言 正文
Redis 性能影響 - 內(nèi)存碎片和緩沖區(qū)
- 一. 內(nèi)存碎片帶來的性能影響
- 1.1 內(nèi)存碎片的形成
- 1.2 清理內(nèi)存碎片
- 1.3 總結
- 二. 內(nèi)存緩沖區(qū)溢出問題
- 2.1 客戶端通信中的緩沖區(qū)
- 2.1.1 輸入緩沖區(qū)溢出和避免
- 2.1.2 輸出緩沖區(qū)溢出和避免
- 2.2 主從集群中的緩沖區(qū)
- 2.2.1 復制緩沖區(qū)溢出和避免
- 2.2.2 復制積壓緩沖區(qū)溢出和避免
- 2.3 總結
一. 內(nèi)存碎片帶來的性能影響
首先,我們需要明確并且知道一點:Redis
中刪除某個數(shù)據(jù)之后,實際上是將Redis
釋放的內(nèi)存空間會由內(nèi)存分配器管理,并不會立即返回給操作系統(tǒng)。因此在一定的時間操作系統(tǒng)仍然記錄著這個刪除的key
占用的內(nèi)存。
問題:Redis
釋放的內(nèi)存不一定是連續(xù)的,因此這種不連續(xù)的內(nèi)存空間就有可能無法拿來保存數(shù)據(jù)。那么就會變相的減少Redis
能夠實際保存的數(shù)據(jù)量。
換句話說:應用程序申請的是一塊連續(xù)的內(nèi)存地址,但是實際上機器提供的卻是內(nèi)存碎片。
1.1 內(nèi)存碎片的形成
內(nèi)存碎片形成的原因主要有2點:
- 內(nèi)因:操作系統(tǒng)的內(nèi)存分配機制。
- 外因:
Redis
的負載特征造成。
首先來說下內(nèi)因:內(nèi)存分配器會按照固定大小來分配內(nèi)存,而不是按需分配。例如Linux
下默認是4KB
,開啟內(nèi)存大頁機制后就變成2MB
。
Redis
中使用jemalloc
分配器來分配內(nèi)存。它會按照一系列固定大小的內(nèi)存來進行分配。例如當Redis
中需要申請一個20B
大小的空間來保存數(shù)據(jù),那么jemalloc
分配器就會分配32B
:
- 倘若此時應用還要寫入
5B
大小的數(shù)據(jù),那么無需申請額外的空間。 - 倘若此時應用還要寫入
20B
大小的數(shù)據(jù),那么必須在申請額外的空間了,此時就會有產(chǎn)生內(nèi)存碎片的風險(之前分配的32B
中,10B
就是內(nèi)存碎片了)
如圖:
緊接著就是外因部分了:
首先我們一個Redis
實例,里面有著不同大小的鍵值對,那么根據(jù)內(nèi)存分配器的分配機制來看。就有可能分配著不同大小的連續(xù)內(nèi)存空間。
另一方面,我們對鍵值對也有可能有著不同的操作,增刪改查。那么看下這個圖:
上圖中,白色部分的就是內(nèi)存碎片,可以看出大小不一的鍵值對以及修改刪除操作導致產(chǎn)生了內(nèi)存碎片。
1.2 清理內(nèi)存碎片
清理內(nèi)存碎片之前,首先應該做的就是判斷是否有內(nèi)存碎片:
info memory
結果如下:
請看我紅色框框圈起來的地方:
mem_fragmentation_ratio:3.22
mem_fragmentation_ratio
代表Redis
實例當前的內(nèi)存碎片率。其計算公式為:
mem_fragmentation_ratio = used_memory_rss / used_memory
-
used_memory_rss
:操作系統(tǒng)實際分配Redis
的物理內(nèi)存空間。 -
used_memory
:Redis
為了保存數(shù)據(jù)而實際申請的空間。
針對mem_fragmentation_ratio
,有兩個參考:
-
mem_fragmentation_ratio
∈ (1, 1.5]
:屬于合理范圍內(nèi),暫時可以放放。 -
mem_fragmentation_ratio
∈ (1.5, +∞)
:表明內(nèi)存碎片率超過了50%
,需要采取措施降低內(nèi)存碎片率。
那么如何清理內(nèi)存碎片呢(一般不會重啟實例,因為生產(chǎn)上往往不允許這種神操作出現(xiàn)),在Redis4.0-RC3
版本以后,Redis
提供了內(nèi)置的內(nèi)存碎片清理機制。
# 開啟自動內(nèi)存碎片清理功能
config set activedefrag yes
開啟自動清理機制之后,需要同時滿足兩個條件才可以觸發(fā)執(zhí)行:
-
active-defrag-ignore-bytes 100mb
:表示內(nèi)存碎片的字節(jié)數(shù)達到100MB
時,開始清理。 -
active-defrag-threshold-lower 10
:表示內(nèi)存碎片空間占操作系統(tǒng)分配給Redis
的總空間比例達到10%
時,開始清理。
除此之外,值得注意的是,雖然Redis
提供了這樣的自動內(nèi)存清理機制,能夠帶來清理內(nèi)存碎片的好處,但是與此同時的必定有著其對應的犧牲,也就是性能影響問題:
- 碎片清理是有代價的:操作系統(tǒng)需要把多份數(shù)據(jù)拷貝到新位置,把原有空間釋放出來,這會帶來時間開銷。
Redis
是單線程,在數(shù)據(jù)拷貝時,Redis
進入阻塞狀態(tài),從而導致Redis
無法及時處理請求,性能就會降低。
因此在開啟自動清理內(nèi)存碎片機制的情況下,需要合理考慮得失。Redis
就提供了另外的兩個參數(shù):控制清理操作占用的 CPU
時間比例的上下限。active-defrag-cycle-min 25
: 表示自動清理過程所用 CPU
時間的比例不低于 25%
,保證清理能正常開展。active-defrag-cycle-max 75
:表示自動清理過程所用 CPU
時間的比例不高于 75%
,一旦超過,就停止清理。
上述4個相關的配置如下(我的機器):
問題來了,如果mem_fragmentation_ratio
的值小于1,代表什么?
前面我們知道,著個指標的計算用大白話來說就是 總物理內(nèi)存 / 實際數(shù)據(jù)所需內(nèi)存
。小于1,代表總物理內(nèi)存不夠數(shù)據(jù)存儲了,那不就開啟swap
機制了嗎!
-
Redis
沒有足夠的物理內(nèi)存可以使用,這會導致Redis
一部分內(nèi)存數(shù)據(jù)會被換到Swap
中。 - 那么之后當
Redis
訪問Swap
中的數(shù)據(jù)時,延遲會變大,性能下降。
那可不行,那如果你的Redis
實例中,這個指標小于1,趕緊加大內(nèi)存,或者考慮搞成Redis
集群!
1.3 總結
到這里為止,文章中個人認為最重要的幾個點是:
- 通過
info memory
命令查看內(nèi)存的使用情況。 -
mem_fragmentation_ratio
,如果超過了1.5,建議可以考慮進行內(nèi)存碎片的清理了。 -
內(nèi)存碎片的清理可以開啟自動清理內(nèi)存碎片機制,是主線程執(zhí)行的,會發(fā)生阻塞。需要合理配置對應的參數(shù),保證
Redis
的高性能。 -
mem_fragmentation_ratio
,如果值小于1,說明物理內(nèi)存不夠真實數(shù)據(jù)的保存了,此時機器應該開啟了swap
機制,會導致Redis
性能的嚴重下降。應該考慮增加機器的內(nèi)存配置了,或者搞集群。
二. 內(nèi)存緩沖區(qū)溢出問題
背景:客戶端和服務端之間,有一個輸入和輸出緩沖區(qū)。主要用來避免請求或者數(shù)據(jù)丟失
。在Redis
中主要有兩個應用場景:
- 就是在客戶端和服務器端之間進行通信時:用來暫存客戶端發(fā)送的命令數(shù)據(jù),或者是服務器端返回給客戶端的數(shù)據(jù)結果。
- 主從節(jié)點間進行數(shù)據(jù)同步時:用來暫存主節(jié)點接收的寫命令和數(shù)據(jù)。
服務器會和每個連接的客戶端都設置一個輸入輸出緩沖區(qū),大概圖如下:
2.1 客戶端通信中的緩沖區(qū)
緩沖區(qū)的功能我們說過:主要就是用一塊內(nèi)存空間來暫時存放命令數(shù)據(jù)。
那么什么是緩沖區(qū)溢出?
倘若如果往里面
寫入數(shù)據(jù)的速度
持續(xù)地大于
從里面讀取數(shù)據(jù)的速度
,就會導致緩沖區(qū)需要越來越多的內(nèi)存來暫存數(shù)據(jù)。當緩沖區(qū)占用的內(nèi)存超出了設定的上限閾值時,就會出現(xiàn)緩沖區(qū)溢出。
2.1.1 輸入緩沖區(qū)溢出和避免
導致輸入緩沖區(qū)溢出的情況主要分為兩種:
-
寫入
bigkey
。 -
服務器端處理請求的速度太慢。比如
Redis
主線程出現(xiàn)了阻塞。
那么如何避免溢出呢?可以使用以下命令來查看服務端和客戶端之間的輸入緩沖區(qū)的使用情況:
client list
結果如下:
id=7392 addr=127.0.0.1:54854 laddr=127.0.0.1:6379 fd=9 name= age=5007 idle=0 flags=N db=0 sub=0 psub=0
multi=-1 qbuf=26 qbuf-free=40928 argv-mem=10 obl=0 oll=0 omem=0
tot-mem=61466 events=r cmd=client user=default redir=-1
圖示:
其中比較重要的幾個參數(shù):
-
addr
:不同客戶端的IP
和端口號。 -
qbuf
:表示輸入緩沖區(qū)已經(jīng)使用的大小。單位字節(jié)。 -
cmd
:客戶端最新執(zhí)行的命令。 -
qbuf-free
:輸入緩沖區(qū)剩余的可用空間大小。單位字節(jié)。
那么倘若 qbuf-free
值太小,就需要引起注意,因為此時一旦寫入大量命令,就容易引起緩沖區(qū)的溢出。
對于輸入緩沖區(qū)。我們可以從兩個方面去考慮避免溢出:
- 輸入緩沖區(qū)規(guī)定上限是
1GB
。因此其無法增大。該方案行不通。 - 從命令和數(shù)據(jù)發(fā)送方考慮:避免客戶端寫入
bigkey
,以及避免Redis
主線程阻塞。
2.1.2 輸出緩沖區(qū)溢出和避免
對于輸出緩沖區(qū),其主要分為兩個部分:
-
固定緩沖空間:
16KB
,用于暫存OK
響應和出錯信息。例如: -
動態(tài)緩沖空間:用來暫存可以變的響應結果。例如:
輸出緩沖區(qū)發(fā)生溢出主要有三種情況:
- 服務器端返回
bigkey
的大量數(shù)據(jù)。 - 執(zhí)行了
monitor
命令。 - 緩沖區(qū)大小設置不合理。
對于bigkey
,無論是輸入還是輸出緩沖區(qū),都會有很大的影響,而且Redis
本身的性能也受其影響,bigkey
相關內(nèi)存的開辟和釋放,都需要很大的資源去消耗。無論從各個方面來考慮,bigkey
的存在都是不建議有的。
然后是monitor
命令,我們來看下他是干啥的:
-
首先準備兩個會話,會話1 輸入
monitor
命令,進入監(jiān)控。 -
另外一個 會話2 可以隨便執(zhí)行幾個命令,如圖:
-
此時 會話1 的監(jiān)控頁面:
-
MONITOR
的輸出結果會持續(xù)占用輸出緩沖區(qū),最后的結果就是發(fā)生溢出。 因此這個命令不要再生產(chǎn)上使用。
其次就是關于輸出緩沖區(qū)大小的設置,這點和輸入緩沖區(qū)并不相同,輸入緩沖區(qū)的大小不可設置。而輸出緩沖區(qū)可以。通過client-output-buffer-limit
來配置。它主要有四個參數(shù):
- 客戶端類型。
- 緩沖區(qū)的大小。
- 緩沖區(qū)持續(xù)寫入的數(shù)據(jù)量限制。
- 緩沖區(qū)持續(xù)寫入的時間限制。
如果在持續(xù)寫入的時間限制內(nèi)寫入的數(shù)據(jù)量超過了規(guī)定的量或者超出輸出緩沖區(qū)規(guī)定的大小,則會關閉客戶端連接。此時最好根據(jù)客戶端的性質來具體配置:
倘若該客戶端是主要進行讀寫命令交互的普通客戶端:
# 主要進行讀寫命令交互的普通客戶端 normal表示普通客戶端,0代表不限制
client-output-buffer-limit normal 0 0 0
倘若該客戶端是訂閱客戶端,即訂閱了 Redis
頻道的訂閱客戶端。
# pubsub代表訂閱客戶端
client-output-buffer-limit pubsub 8mb 2mb 60
上述配置代表:
- 實際占用緩沖區(qū)的大小 >
8MB
,服務端就會關閉與該客戶端的連接。 -
60s
內(nèi)如果持續(xù)對輸出緩沖區(qū)寫入超過2MB
的數(shù)據(jù),同樣關閉與客戶端之間的連接。
2.2 主從集群中的緩沖區(qū)
主從集群之間主要的操作還是在于數(shù)據(jù)的復制,而數(shù)據(jù)的復制分為兩種:
-
全量復制。
-
增量復制。
2.2.1 復制緩沖區(qū)溢出和避免
這一塊主要發(fā)生在全量復制這個階段,主庫在向從庫傳輸RDB
文件的同時,會將客戶端發(fā)送的寫命令請求保存到復制緩沖區(qū)中。等待文件傳輸完畢,在發(fā)送給從庫去執(zhí)行。如圖:
因此,倘若RDB
傳輸過程比較久,同時傳輸期間主庫又接收到大量的寫命令,從而導致復制緩沖區(qū)中的命令越來越多,最后導致溢出。
解決:
- 控制主節(jié)點保存的數(shù)據(jù)大小。避免全量同步執(zhí)行速度太慢。
- 使用
client-output-buffer-limit
命令,設置復制緩沖區(qū)大小。
我們可以看下復制緩沖區(qū)相關的配置:
config get client-output-buffer-limit
結果如下:
還記得 2.1.2 小節(jié)中對于輸出緩沖區(qū)的設置嗎?我們可以發(fā)現(xiàn)用的是同一個命令,只不過有以下區(qū)別:
-
pubsub
:就是訂閱客戶端。 -
normal
:常規(guī)的客戶觀(進行簡單的命令交互)。 -
slave
:該配置項就是針對復制緩沖區(qū)的。
那么設置的時候我們可以這么來:
# 設置的時候請加上slave,因為一共有三種,這里需要指定的是復制緩沖區(qū)的配置
# 復制緩沖區(qū)上限為512mb,超過了就斷開連接
# 2分鐘內(nèi)寫入的數(shù)據(jù)超過64mb,斷開連接
config set client-output-buffer-limit slave 512mb 64mb 120
2.2.2 復制積壓緩沖區(qū)溢出和避免
復制積壓緩沖區(qū)(即repl_backlog_buffer
,在Redis - Redis主從數(shù)據(jù)一致性和哨兵機制中有提到)主要作用于增量復制:
- 主節(jié)點在把接收到的寫命令同步給從節(jié)點時,同時會把這些寫命令寫入復制積壓緩沖區(qū)。
- 一旦從節(jié)點發(fā)生宕機,當重啟后,從節(jié)點就會從復制積壓緩沖區(qū)中,讀取斷連期間主節(jié)點接收到的寫命令,進而進行增量同步。
如圖:
因為在相關文章中講到過了,這里就截個圖:
因此可以適當調(diào)大一點這個值。
2.3 總結
文章提到了4個緩沖區(qū)。
- 輸入緩沖區(qū):保存客戶端發(fā)送的命令。
- 輸出緩沖區(qū):保存服務端返回的數(shù)據(jù)。
- 復制緩沖區(qū):用于全量復制時,保存新寫入的命令。
- 復制積壓緩沖區(qū):用于增量復制時,保存新寫入的命令
從文章的內(nèi)容來看:
- 服務端和每個客戶端之間都存在對應的幾種緩沖區(qū)。
- 當發(fā)生了緩沖區(qū)溢出,服務端會斷開與對應客戶端之間的連接。
- 此時會導致朱從節(jié)點同步失敗,或者程序無法讀寫
Redis
。
緩沖區(qū)溢出,可以從三個方面來分別解決:
命令數(shù)據(jù)發(fā)送太快。
- 與普通客戶端:避免
bigkey
。 - 復制緩沖區(qū):避免大型
RDB
文件的產(chǎn)生。
命令數(shù)據(jù)處理太慢:減少Redis
主線程上的阻塞操作。可以有針對的利用異步機制。
緩沖區(qū)太小了:
- 使用
client-output-buffer-limit
進行相關配置,主要配置輸出緩沖區(qū)和復制緩沖區(qū)。 - 對
repl_backlog_buffer
進行相關配置,增大復制積壓緩沖區(qū)。 - 輸入緩沖區(qū)大小無法修改。
- 避免在生產(chǎn)上使用
monitor
監(jiān)控命令。
原文鏈接:https://blog.csdn.net/Zong_0915/article/details/126302182
相關推薦
- 2022-07-13 JMeter主要元件_線程組的使用方法
- 2022-09-04 React18中請求數(shù)據(jù)的官方姿勢適用其他框架_React
- 2022-08-01 flask上使用websocket的方法示例_python
- 2022-08-10 python數(shù)組中的?k-diff?數(shù)對例題解析_python
- 2022-07-14 Python實現(xiàn)單例模式的四種方式詳解_python
- 2022-11-07 WPF使用DrawingContext實現(xiàn)繪制刻度條_C#教程
- 2022-06-21 C語言容易被忽視的函數(shù)設計原則基礎_C 語言
- 2022-08-05 C#實現(xiàn)鐘表程序設計_C#教程
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細win安裝深度學習環(huán)境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結構-簡單動態(tài)字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支