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