網(wǎng)站首頁 編程語言 正文
前言
我們先來看一個(gè)問題, 假設(shè)Redis實(shí)例保存了5GB的數(shù)據(jù),現(xiàn)在刪除了2GB的數(shù)據(jù),那么Redis進(jìn)程占用的內(nèi)存會(huì)不會(huì)減少呢?
答案是:它可能仍然占用大約5GB內(nèi)存,即使Redis數(shù)據(jù)只占用大約3GB。
如果maxmemory
不設(shè)置該參數(shù),Redis不會(huì)觸發(fā)內(nèi)存淘汰策略刪除數(shù)據(jù)。
Redis會(huì)繼續(xù)為新寫入的數(shù)據(jù)分配內(nèi)存。分配失敗會(huì)導(dǎo)致應(yīng)用程序報(bào)錯(cuò),當(dāng)然不會(huì)導(dǎo)致宕機(jī)。
注:設(shè)置maxmemory
參數(shù),執(zhí)行命令CONFIG SET maxmemory 100mb
,或在redis.conf
配置文件中設(shè)置maxmemory 100mb。
使用top命令查看數(shù)據(jù)是否已經(jīng)刪除,為什么它仍然占用這么多內(nèi)存?
釋放的內(nèi)存去了哪里?
當(dāng)我們使用top命令查看系統(tǒng)使用情況時(shí),會(huì)發(fā)現(xiàn)內(nèi)存依然很高,Redis并沒有真正釋放內(nèi)存。那么內(nèi)存都去哪兒了?這時(shí)候我們就需要使用info memory
命令獲取Redis內(nèi)存相關(guān)的指標(biāo)。
127.0.0.1:6379> info memory # Memory used_memory:1132832 // Redis Amount of memory used to store data used_memory_human:1.08M // Returns the total amount of memory in human readable form used_memory_rss:2977792 // From the perspective of the operating system, the total physical memory occupied by the process used_memory_rss_human:2.84M // used_memory_rss Readability mode display used_memory_peak:1183808 // The maximum value of memory used, representing the peak value of used_memory used_memory_peak_human:1.13M // Returns the value of used_memory_peak in a human readable format used_memory_lua:37888 // Lua The amount of memory consumed by the engine。 used_memory_lua_human:37.00K maxmemory:2147483648 // The maximum memory value that can be used, in bytes. maxmemory_human:2.00G // readable form maxmemory_policy:noeviction // Memory Retirement Policy mem_fragmentation_ratio:2.79 // The ratio of used_memory_rss & used_memory represents the memory fragmentation rate
Redis進(jìn)程內(nèi)存消耗主要由以下幾部分組成:
- 內(nèi)存被Redis自己啟動(dòng)占用
- 存儲(chǔ)對象數(shù)據(jù)內(nèi)存
- 緩沖區(qū)內(nèi)存:主要由
client-output-buffer-limit
客戶端輸出緩沖區(qū)、copy backlog
緩沖區(qū)、AOF
緩沖區(qū)組成 - 內(nèi)存碎片
Redis自身的空進(jìn)程占用的內(nèi)存很小,可以忽略不計(jì),而對象內(nèi)存是最大的,里面存放了所有的數(shù)據(jù)。
需要注意, 如果緩沖區(qū)有大流量的場景很容易失控,導(dǎo)致Redis內(nèi)存不穩(wěn)定。
內(nèi)存碎片過多導(dǎo)致有可用空間不足,無法存儲(chǔ)數(shù)據(jù)。內(nèi)存碎片Fragmentation
= used_memory_rss
實(shí)際使用的物理內(nèi)存(RSS 值)除以 used_memory
實(shí)際存儲(chǔ)的數(shù)據(jù)內(nèi)存。
什么是內(nèi)存碎片?
內(nèi)存碎片會(huì)導(dǎo)致內(nèi)存空間空閑,但無法存儲(chǔ)數(shù)據(jù)。比如你和女朋友去電影院看電影,你們肯定是要在一起的。
假設(shè)現(xiàn)在有 8 個(gè)座位,已售出 4 張票,還有 4 張可供購買。不過巧合的是,買票的人很奇怪,都是隔一個(gè)座位買票。即使還有4個(gè)座位,也不能買順序連接兩個(gè)座位的票。
什么導(dǎo)致內(nèi)存碎片?
主要有兩個(gè)原因:
- 內(nèi)存分配器的分配策略
- 鍵值對的大小不同,刪除操作
下面我們就實(shí)際發(fā)生的原因進(jìn)行探討。
1. 內(nèi)存分配器的分配策略
Redis 默認(rèn)的內(nèi)存分配器使用jemalloc
,可選的分配器有:glibc
,tcmalloc
。
內(nèi)存分配器不能按需分配,而是使用固定范圍的內(nèi)存塊進(jìn)行分配。
比如8字節(jié)、16字節(jié)……、2KB、4KB,當(dāng)申請內(nèi)存最接近固定值時(shí),jemalloc
會(huì)分配最接近固定值的空間。這樣就會(huì)出現(xiàn)內(nèi)存碎片。
比如程序只需要1.5KB,而內(nèi)存分配器會(huì)分配2KB的空間,那么這0.5KB就是碎片。這樣做的目的是減少內(nèi)存分配的次數(shù)。比如你申請22個(gè)字節(jié)的空間來存放數(shù)據(jù),jemalloc就會(huì)分配32個(gè)字節(jié)。如果后面需要寫入10個(gè)字節(jié),則不需要向操作系統(tǒng)申請空間。您可以使用之前請求的 32 個(gè)字節(jié)。
當(dāng)一個(gè)鍵被刪除時(shí),Redis 不會(huì)立即將內(nèi)存歸還給操作系統(tǒng)。發(fā)生這種情況是因?yàn)榈讓觾?nèi)存分配器的管理。例如,大多數(shù)已刪除的鍵仍與其他有效鍵分配在同一內(nèi)存頁中。
此外,為了重用空閑內(nèi)存塊,分配器刪除了原始 5 GB 數(shù)據(jù)中的 2 GB。再次向?qū)嵗砑訑?shù)據(jù)時(shí),Redis的RSS會(huì)保持穩(wěn)定,不會(huì)增加太多。因?yàn)閮?nèi)存分配器基本上重新使用了之前刪除釋放的2GB內(nèi)存。
2.鍵值對大小不同,刪除操作
由于內(nèi)存分配器是按照固定的大小分配內(nèi)存,因此分配的內(nèi)存空間通常會(huì)大于實(shí)際數(shù)據(jù)占用的大小,這會(huì)造成碎片,降低內(nèi)存的存儲(chǔ)效率。
另外,鍵值對的頻繁修改和刪除導(dǎo)致內(nèi)存空間的擴(kuò)大和釋放。例如,如果原來占用32個(gè)字節(jié)的字符串現(xiàn)在修改為占用20個(gè)字節(jié)的字符串,那么釋放的12個(gè)字節(jié)就是空閑空間。
如果下一次數(shù)據(jù)存儲(chǔ)請求需要申請一個(gè)13字節(jié)的字符串,剛剛釋放的12字節(jié)空間就不能使用,造成碎片。
分片最大的問題:空間總量足夠大,但是這些內(nèi)存并不連續(xù),可能存不下數(shù)據(jù)。
mem_fragmentation_ratio = used_memory_rss/ used_memory
如何解決?
首先要判斷是否發(fā)生了內(nèi)存碎片,重點(diǎn)看info memory
命令執(zhí)行后的mem_fragmentation_ratio
指標(biāo),表示內(nèi)存碎片率。
如果1 < mem_fragmentation_ratio < 1.5
,可以認(rèn)為是合理的,如果大于1.5,說明碎片已經(jīng)超過了50%,我們需要采取一些措施來解決碎片過多的問題。
1. 重啟
最簡單的方法是重新啟動(dòng)。如果未啟用持久性,數(shù)據(jù)將丟失。
如果開啟持久化,需要使用RDB或者AOF來恢復(fù)數(shù)據(jù)。如果只有一個(gè)實(shí)例,數(shù)據(jù)量大會(huì)導(dǎo)致恢復(fù)階段長時(shí)間無法提供服務(wù),高可用性會(huì)大大降低。
2.自動(dòng)清理內(nèi)存碎片
Redis在4.0版本之后,提供了內(nèi)存碎片清理機(jī)制。
對于Redis來說,當(dāng)連續(xù)的內(nèi)存空間被分割成若干個(gè)不連續(xù)的空間時(shí),操作系統(tǒng)首先將數(shù)據(jù)移動(dòng)拼接在一起,釋放掉原來數(shù)據(jù)占用的空間,形成一個(gè)連續(xù)的空閑內(nèi)存空間。
圖片由作者提供
自動(dòng)清理雖然好,但也不要亂來。操作系統(tǒng)需要消耗資源將數(shù)據(jù)移動(dòng)到新的位置,然后釋放原來的空間。
Redis 操作數(shù)據(jù)的指令是單線程的,所以在數(shù)據(jù)復(fù)制和移動(dòng)時(shí),只有清理碎片后才能處理請求,會(huì)造成性能損失。
那么問題來了,如何減少對性能的影響來實(shí)現(xiàn)自動(dòng)清理碎片?
問得好,用下面兩個(gè)參數(shù)來控制內(nèi)存碎片清理和結(jié)束的時(shí)機(jī),避免占用過多CPU,減少清理碎片對Redis處理請求的性能影響。
啟用自動(dòng)內(nèi)存碎片整理:
CONFIG SET activedefrag yes
這只是為了啟用自動(dòng)清潔。當(dāng)清理需要同時(shí)滿足以下兩個(gè)條件時(shí),就會(huì)出發(fā)清理操作。
- 清理?xiàng)l件
active-defrag-ignore-bytes 200mb
:內(nèi)存碎片占用內(nèi)存達(dá)到200MB,開始清理;
active-defrag-threshold-lower 20
: 內(nèi)存碎片空間超過系統(tǒng)分配給Redis空間的20%,開始清理。
- 避免性能影響
清理時(shí)間是有的,需要控制清理對性能的影響。一二設(shè)置先分配清理碎片占用的CPU資源,保證碎片可以正常清理,避免對Redis處理請求造成性能影響。
active-defrag-cycle-min 20
:自動(dòng)碎片整理過程中占用CPU時(shí)間比例不低于20%,以保證清理任務(wù)正常進(jìn)行。
active-defrag-cycle-max 50
:自動(dòng)清理進(jìn)程占用CPU時(shí)間比例不能高于50%。如果超過,會(huì)立即停止清理,避免阻塞 Redis 造成高延遲。
總結(jié)
如果發(fā)現(xiàn)Redis存儲(chǔ)數(shù)據(jù)占用的內(nèi)存比操作系統(tǒng)分配給Redis的內(nèi)存小很多,但是數(shù)據(jù)無法保存,那么可能是內(nèi)存碎片很多。使用info memory
命令查看內(nèi)存碎片mem_fragmentation_ratio
指標(biāo)是否正常。
然后我們啟用自動(dòng)清理并合理設(shè)置清理時(shí)間和CPU資源占用。該機(jī)制涉及內(nèi)存復(fù)制,這對 Redis 性能構(gòu)成潛在風(fēng)險(xiǎn)。如果Redis性能變慢,檢查是否是清理碎片導(dǎo)致的。如果是這樣,減小配置active-defrag-cycle-max
的值。
原文鏈接:https://juejin.cn/post/7194702522412433464
相關(guān)推薦
- 2022-10-11 C++函數(shù)對象Functor與匿名函數(shù)對象Lambda表達(dá)式詳解_C 語言
- 2022-03-10 通過容器擴(kuò)展屬性IExtenderProvider實(shí)現(xiàn)WinForm通用數(shù)據(jù)驗(yàn)證組件_C#教程
- 2022-12-01 C++中單鏈表操作的示例代碼_C 語言
- 2022-08-17 python可視化分析繪制帶趨勢線的散點(diǎn)圖和邊緣直方圖_python
- 2022-07-08 C#中的GDI+圖像編程詳解_C#教程
- 2023-03-30 官網(wǎng)項(xiàng)目Jetpack?Startup庫學(xué)習(xí)_Android
- 2023-05-30 Jquery使用原生AJAX方法請求數(shù)據(jù)_jquery
- 2021-11-25 使用Oracle命令進(jìn)行數(shù)據(jù)庫備份與還原_oracle
- 最近更新
-
- 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)證過濾器
- Spring Security概述快速入門
- 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)-簡單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支