<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          為什么Redis的數(shù)據(jù)被刪除,內(nèi)存占用還這么大?

          共 5084字,需瀏覽 11分鐘

           ·

          2022-10-31 21:44

          ?

          操作系統(tǒng)分配給 Redis 的內(nèi)存有 6GB,通過(guò)指標(biāo) used_memory_human 發(fā)現(xiàn)存儲(chǔ)數(shù)據(jù)只使用了 4GB,為何會(huì)這樣?為何無(wú)法保存數(shù)據(jù)?

          通過(guò) CONFIG SET maxmemory 100mb或者在 redis.conf 配置文件設(shè)置 maxmemory 100mb Redis 內(nèi)存占用限制。當(dāng)達(dá)到內(nèi)存最大值,會(huì)觸發(fā)內(nèi)存淘汰策略刪除數(shù)據(jù)。

          除此之外,當(dāng) key 達(dá)到過(guò)期時(shí)間,Redis 會(huì)有以下兩種刪除過(guò)期數(shù)據(jù)的策略:

          • 后臺(tái)定時(shí)任務(wù)選取部分?jǐn)?shù)據(jù)刪除;
          • 惰性刪除。

          具體原理請(qǐng)移步《Redis 的過(guò)期數(shù)據(jù)刪除那些事》

          ?

          假設(shè) Redis 實(shí)例保存了 5GB 的數(shù)據(jù),現(xiàn)在刪除了 2GB 數(shù)據(jù),Redis 進(jìn)程占用的內(nèi)存一定會(huì)降低么?(也叫做 RSS,進(jìn)程消耗內(nèi)存頁(yè)數(shù))。

          答案是:可能依然占用了大約 5GB 的內(nèi)存,即使 Redis 的數(shù)據(jù)只占用了 3GB 左右。

          大家一定要設(shè)置maxmemory,否則 Redis 會(huì)繼續(xù)為新寫入的數(shù)據(jù)分配內(nèi)存,無(wú)法分配就會(huì)導(dǎo)致應(yīng)用程序報(bào)錯(cuò),當(dāng)然不會(huì)導(dǎo)致宕機(jī)。

          釋放的內(nèi)存去哪了

          ?

          明明刪除了數(shù)據(jù),使用 top 命令查看,為何還是占用了那么多內(nèi)存?

          內(nèi)存都去哪了?使用 info memory 命令獲取 Redis 內(nèi)存相關(guān)指標(biāo),我列舉了幾個(gè)重要的數(shù)據(jù):

                127.0.0.1:6379>?info?memory
          #?Memory
          used_memory:1132832??//?Redis?存儲(chǔ)數(shù)據(jù)占用的內(nèi)存量
          used_memory_human:1.08M??//?人類可讀形式返回內(nèi)存總量
          used_memory_rss:2977792??//?操作系統(tǒng)角度,進(jìn)程占用的物理總內(nèi)存
          used_memory_rss_human:2.84M?//?used_memory_rss?可讀性模式展示
          used_memory_peak:1183808?//?內(nèi)存使用的最大值,表示?used_memory?的峰值

          used_memory_peak_human:1.13M??//?以可讀的格式返回?used_memory_peak的值
          used_memory_lua:37888 ??// Lua 引擎所消耗的內(nèi)存大小。
          used_memory_lua_human:37.00K
          maxmemory:2147483648 ???//?能使用的最大內(nèi)存值,字節(jié)為單位。
          maxmemory_human:2.00G??//?可讀形式
          maxmemory_policy:noeviction?//?內(nèi)存淘汰策略

          //?used_memory_rss?/?used_memory?的比值,代表內(nèi)存碎片率
          mem_fragmentation_ratio:2.79

          Redis 進(jìn)程內(nèi)存消耗主要由以下部分組成:

          • Redis 自身啟動(dòng)所占用的內(nèi)存;
          • 存儲(chǔ)對(duì)象數(shù)據(jù)內(nèi)存;
          • 緩沖區(qū)內(nèi)存:主要由 client-output-buffer-limit 客戶端輸出緩沖區(qū)、復(fù)制積壓緩沖區(qū)、AOF 緩沖區(qū)。
          • 內(nèi)存碎片。
          c384a6e665b513f2e9a7534e645560ec.webp內(nèi)存占用

          Redis 自身空進(jìn)程占用的內(nèi)存很小可以忽略不計(jì),對(duì)象內(nèi)存是占比最大的一塊,里面存儲(chǔ)著所有的數(shù)據(jù)。

          緩沖區(qū)內(nèi)存在大流量場(chǎng)景容易失控,造成 Redis 內(nèi)存不穩(wěn)定,需要重點(diǎn)關(guān)注。

          內(nèi)存碎片過(guò)大會(huì)導(dǎo)致明明有空間可用,但是卻無(wú)法存儲(chǔ)數(shù)據(jù)。

          碎片 = used_memory_rss 實(shí)際使用的物理內(nèi)存(RSS 值)除以 used_memory 實(shí)際存儲(chǔ)數(shù)據(jù)內(nèi)存。

          什么是內(nèi)存碎片

          內(nèi)存碎片會(huì)造成明明有內(nèi)存空間空閑,可是卻無(wú)法存儲(chǔ)數(shù)據(jù)。舉個(gè)例子,你跟漂亮小姐姐去電影院看電影,肯定想連在一塊。

          假設(shè)現(xiàn)在有 8 個(gè)座位,已經(jīng)賣出了 4 張票,還有 4 張可以買。可是好巧不巧,買票的人很奇葩,分別間隔一個(gè)座位買票。

          即使還有 4 個(gè)座位空閑,可是你卻買不到兩個(gè)座位連在一塊的票,厚禮蟹!

          1a12b339ea5cb4700348bc4b3e047062.webp內(nèi)存碎片

          內(nèi)存碎片形成原因

          ?

          內(nèi)存碎片是什么原因?qū)е履兀?/p>

          主要有兩個(gè)原因:

          • 內(nèi)存分配器的分配策略。
          • 鍵值對(duì)的大小不一樣和刪改操作:Redis 頻繁做更新操作、大量過(guò)期數(shù)據(jù)刪除,釋放的空間(不夠連續(xù))無(wú)法得到復(fù)用,導(dǎo)致碎片率上升。

          接下來(lái)我分別探討實(shí)際發(fā)生的原因……

          內(nèi)存分配器的分配策略

          Redis 默認(rèn)的內(nèi)存分配器采用 jemalloc,可選的分配器還有:glibc、tcmalloc。

          內(nèi)存分配器并不能做到按需分配,而是采用固定范圍的內(nèi)存塊進(jìn)行分配。

          例如 8 字節(jié)、16 字節(jié)…..,2 KB,4KB,當(dāng)申請(qǐng)內(nèi)存最近接某個(gè)固定值的時(shí)候,jemalloc 會(huì)給它分配最接近固定值大小的空間。

          這樣就會(huì)出現(xiàn)內(nèi)存碎片,比如程序只需要 1.5 KB,內(nèi)存分配器會(huì)分配 2KB 空間,那么這 0.5KB 就是碎片。

          這么做的目的是減少內(nèi)存分配次數(shù),比如申請(qǐng) 22 字節(jié)的空間保存數(shù)據(jù),jemalloc 就會(huì)分配 32 字節(jié),如果后邊還要寫入 10 字節(jié),就不需要再向操作系統(tǒng)申請(qǐng)空間了,可以使用之前申請(qǐng)的 32 字節(jié)。

          刪除 key 的時(shí)候,Redis 并不會(huì)立馬把內(nèi)存歸還給操作系統(tǒng),出現(xiàn)這個(gè)情況是因?yàn)榈讓觾?nèi)存分配器管理導(dǎo)致,比如大多數(shù)已經(jīng)刪除的 key 依然與其他有效的 key 分配在同一個(gè)內(nèi)存頁(yè)中。

          另外,分配器為了復(fù)用空閑的內(nèi)存塊,原有 5GB 的數(shù)據(jù)中刪除了 2 GB 后,當(dāng)再次添加數(shù)據(jù)到實(shí)例中,Redis 的 RSS 會(huì)保持穩(wěn)定,不會(huì)增長(zhǎng)太多。

          因?yàn)?strong style="font-weight:bold;color:rgba(0,0,0,.85);">內(nèi)存分配器基本上復(fù)用了之前刪除釋放出來(lái)的 2GB 內(nèi)存。

          鍵值對(duì)大小不一樣和刪改操作

          由于內(nèi)存分配器是按照固定大小分配內(nèi)存,所以通常分配的內(nèi)存空間比實(shí)際數(shù)據(jù)占用的大小多一些,會(huì)造成碎片,降低內(nèi)存的存儲(chǔ)效率。

          另外,鍵值對(duì)的頻繁修改和刪除,導(dǎo)致內(nèi)存空間的擴(kuò)容和釋放,比如原本占用 32 字節(jié)的字符串,現(xiàn)在修改為占用 20 字節(jié)的字符串,那么釋放出的 12 字節(jié)就是空閑空間。

          如果下一個(gè)數(shù)據(jù)存儲(chǔ)請(qǐng)求需要申請(qǐng) 13 字節(jié)的字符串,那么剛剛釋放的 12 字節(jié)空間無(wú)法使用,導(dǎo)致碎片。

          碎片最大的問(wèn)題:空間總量足夠大,但是這些內(nèi)存不是連續(xù)的,可能大致無(wú)法存儲(chǔ)數(shù)據(jù)。

          內(nèi)存碎片解決之道

          ?

          那該如何解決呢?

          首先要確定是否發(fā)生了內(nèi)存碎片,重點(diǎn)關(guān)注前面 INFO memory 命令提示的 mem_fragmentation_ratio 指標(biāo),表示內(nèi)存碎片率:

                mem_fragmentation_ratio?=?used_memory_rss/?used_memory

          如果 1 < 碎片率 < 1.5,可以認(rèn)為是合理的,而大于 1.5 說(shuō)明碎片已經(jīng)超過(guò) 50%,我們需要采取一些手段解決碎片率過(guò)大的問(wèn)題。

          重啟大法

          最簡(jiǎn)單粗暴的方式就是重啟,如果沒(méi)有開(kāi)啟持久化,數(shù)據(jù)會(huì)丟失。

          開(kāi)啟持久化的話,需要使用 RDB 或者 AOF 恢復(fù)數(shù)據(jù),如果只有一個(gè)實(shí)例,數(shù)據(jù)大的話會(huì)導(dǎo)致恢復(fù)階段長(zhǎng)時(shí)間無(wú)法提供服務(wù),高可用大打折扣。

          ?

          咋辦呢?碼哥靚仔

          自動(dòng)清理內(nèi)存碎片

          既然你都叫我靚仔了,就傾囊相助告訴你終極殺招:Redis 4.0 版本后,自身提供了一種內(nèi)存碎片清理機(jī)制。

          ?

          怎么清理呢?

          很簡(jiǎn)單,還是上面的例子,想要買兩張連在一塊的電影票。與與別人溝通調(diào)換下位置,就實(shí)現(xiàn)了。

          對(duì)于 Redis 來(lái)說(shuō),當(dāng)一塊連續(xù)的內(nèi)存空間被劃分為好幾塊不連續(xù)的空間的時(shí)候,操作系統(tǒng)先把數(shù)據(jù)以依次挪動(dòng)拼接在一塊,并釋放原來(lái)數(shù)據(jù)占據(jù)的空間,形成一塊連續(xù)空閑內(nèi)存空間。。

          如下圖所示:

          dd33a2223340f5662906c427f788fa5a.webp碎片清理

          自動(dòng)清理內(nèi)存碎片的代價(jià)

          自動(dòng)清理雖好,可不要肆意妄為,操作系統(tǒng)把數(shù)據(jù)移動(dòng)到新位置,再把原有空間釋放是需要消耗資源的。

          Redis 操作數(shù)據(jù)的指令是單線程,所以在數(shù)據(jù)復(fù)制移動(dòng)的時(shí)候,只能等待清理碎片完成才能處理請(qǐng)求,造成性能損耗。

          ?

          如何避免清理碎片對(duì)性能的影響又能實(shí)現(xiàn)自動(dòng)清理呢?

          好問(wèn)題,通過(guò)以下兩個(gè)參數(shù)來(lái)控制內(nèi)存碎片清理和結(jié)束時(shí)機(jī),避免占用 CPU 過(guò)多,減少清理碎片對(duì) Redis 處理請(qǐng)求的性能影響。

          開(kāi)啟自動(dòng)內(nèi)存碎片清理

                CONFIG?SET?activedefrag?yes

          這只是開(kāi)啟自動(dòng)清理,何時(shí)清理要同時(shí)滿足以下兩個(gè)條件才會(huì)觸發(fā)清理操作。

          清理的條件

          active-defrag-ignore-bytes 200mb:內(nèi)存碎片占用的內(nèi)存達(dá)到 200MB,開(kāi)始清理;

          active-defrag-threshold-lower 20:內(nèi)存碎片的空間占比超過(guò)系統(tǒng)分配給 Redis 空間的 20% ,開(kāi)始清理。

          避免對(duì)性能造成影響

          清理時(shí)間有了,還需要控制清理對(duì)性能的影響。由一項(xiàng)兩個(gè)設(shè)置先分配清理碎片占用的 CPU 資源,保證既能正常清理碎片,又能避免對(duì) Redis 處理請(qǐng)求的性能影響。

          active-defrag-cycle-min 20:自動(dòng)清理過(guò)程中,占用 CPU 時(shí)間的比例不低于 20%,從而保證能正常展開(kāi)清理任務(wù)。

          active-defrag-cycle-max 50:自動(dòng)清理過(guò)程占用的 CPU 時(shí)間比例不能高于 50%,超過(guò)的話就立刻停止清理,避免對(duì) Redis 的阻塞,造成高延遲。

          總結(jié)

          如果你發(fā)現(xiàn)明明 Redis 存儲(chǔ)數(shù)據(jù)的內(nèi)存占用遠(yuǎn)小于操作系統(tǒng)分配給 Redis 的內(nèi)存,而又無(wú)法保存數(shù)據(jù),那可能出現(xiàn)大量?jī)?nèi)存碎片了。

          通過(guò) info memory 命令,看下內(nèi)存碎片mem_fragmentation_ratio 指標(biāo)是否正常。

          那么我們就開(kāi)啟自動(dòng)清理并合理設(shè)置清理時(shí)機(jī)和 CPU 資源占用,該機(jī)制涉及到內(nèi)存拷貝,會(huì)對(duì) Redis 性能造成潛在風(fēng)險(xiǎn)。

          如果遇到 Redis 性能變慢,排查下是否由于清理碎片導(dǎo)致,如果是,那就調(diào)小 active-defrag-cycle-max 的值。

          最后,可以我叫我一聲靚仔么?你有什么問(wèn)題想對(duì)哥說(shuō)么?在留言區(qū)留言吧,知無(wú)不言。

          參考

          [1].Redis 核心技術(shù)與實(shí)戰(zhàn) [2].https://juejin.cn/post/6844903967298682893#heading-4 [3].https://redis.io/docs/reference/optimization/memory-optimization/#memory-allocation

          點(diǎn)贊、收藏、分享走起!

          瀏覽 69
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  59丨人妻丨偷拍 | 国产高清性 | 久久婷婷综合视频 | 日韩高清一级片 | 国产91网 |