8.1斗锭、內(nèi)存消耗

Redis所有的數(shù)據(jù)都存在內(nèi)存中岖是,當(dāng)前內(nèi)存雖然越來(lái)越便宜豺撑,但跟廉價(jià)的硬盤(pán)相比成本還是比較昂貴,因此如何高效利用Redis內(nèi)存變得非常重要荧止。高效利用Redis內(nèi)存首先需要理解Redis內(nèi)存消耗在哪里阶剑,如何管理內(nèi)存牧愁,最后才能考慮如何優(yōu)化內(nèi)存猪半。掌握這些知識(shí)后能夠?qū)崿F(xiàn)用更少的內(nèi)存存儲(chǔ)更多的數(shù)據(jù)兔朦,從而減低成本。本章主要內(nèi)容如下:

  • 內(nèi)存消耗分析磨确。
  • 管理內(nèi)存的原理與方法沽甥。
  • 內(nèi)存優(yōu)化技巧。
  1. 內(nèi)存消耗

    理解Redis內(nèi)存乏奥,首先需要掌握Redis內(nèi)存消耗在哪些方面摆舟。有些內(nèi)存消耗是必不可少的,而有些可以通過(guò)參數(shù)調(diào)整和合理使用使用來(lái)規(guī)避內(nèi)存浪費(fèi)邓了。內(nèi)存消耗可以分為進(jìn)程自身消耗和子進(jìn)程消耗恨诱。

    1. 內(nèi)存使用統(tǒng)計(jì)

      首先需要了解Redis自身使用內(nèi)存的統(tǒng)計(jì)數(shù)據(jù),可通過(guò)執(zhí)行info memory命令獲取內(nèi)存相關(guān)指標(biāo)骗炉。讀懂每個(gè)指標(biāo)有助于分析Redis內(nèi)存使用情況照宝,下表列舉出內(nèi)存統(tǒng)計(jì)指標(biāo)和對(duì)應(yīng)解釋。

      屬性名 屬性說(shuō)明旨别、
      used_memory Redis分配器分配的內(nèi)存總量,也就是內(nèi)部存儲(chǔ)的所有數(shù)據(jù)內(nèi)存占用量
      used_memory_human 以可讀的格式返回used_memory
      used_memory_rss 從操作系統(tǒng)的角度顯示Redis進(jìn)程占用的物理內(nèi)存總量
      used_memory_peak 內(nèi)存使用的最大值递览,表示used_memory的峰值
      used_memory_peak_human 以可讀的格式返回used_memory_peak
      used_memory_lua Lua引擎所消耗的內(nèi)存大小
      mem_fragmentation_ratio used_memory_rss/used_memory比值嫂侍,表示內(nèi)存碎片率
      mem_allocator Redis所使用的內(nèi)存分配器菲盾。默認(rèn)為jemalloc

      需要重點(diǎn)關(guān)注的指標(biāo)有:used_memory_rss和used_memory以及它們的比值mem_fragmentation_ratio碎浇。

      當(dāng)mem_fragmentation_ratio > 1時(shí)吴裤,說(shuō)明used_memory_rss-used_memory多出的部分內(nèi)存并沒(méi)有用于數(shù)據(jù)存儲(chǔ),而是被內(nèi)存碎片所消耗,如果兩者相差很大吱晒,說(shuō)明碎片率嚴(yán)重叹话。

      當(dāng)mem_fragmentation_ratio < 1時(shí),這種情況一般出現(xiàn)在操作系統(tǒng)吧Redis內(nèi)存交換(Swap)到硬盤(pán)導(dǎo)致,出現(xiàn)這種請(qǐng)時(shí)要格外關(guān)注般妙,由于硬盤(pán)速度遠(yuǎn)遠(yuǎn)慢于內(nèi)存,Redis性能會(huì)變得很差苫拍,甚至僵死浆洗。

    2. 內(nèi)存消耗劃分

      Redis進(jìn)程內(nèi)存消耗主要包括:自身內(nèi)存 + 對(duì)象內(nèi)存 + 緩沖內(nèi)存 + 內(nèi)存碎片抠刺,其中Redis空進(jìn)程自身內(nèi)存消耗非常少聪黎,通常used_memory_rss在3MB左右锦秒,used_memory在800KB左右侣姆,一個(gè)空的Redis進(jìn)程消耗內(nèi)存可以忽略不計(jì)川蒙。Redis主要內(nèi)存消耗如下圖所示。

      2019-04-29-21-15-48.png

      下面介紹另外三種內(nèi)存消耗:

      1. 對(duì)象內(nèi)存

        對(duì)象內(nèi)存是Redis內(nèi)存占用最大的一塊斤斧,存儲(chǔ)著用戶(hù)所以的數(shù)據(jù)。Redis所有的數(shù)據(jù)都采用key-value數(shù)據(jù)類(lèi)型甘苍,每次創(chuàng)建鍵值對(duì),至少創(chuàng)建兩個(gè)類(lèi)型對(duì)象:key對(duì)象和value對(duì)象。對(duì)象內(nèi)存消耗可以簡(jiǎn)單理解為sizeof(keys) + sizeof(values)。鍵對(duì)象都是字符串谓松,在使用Redis時(shí)很容易忽略鍵對(duì)內(nèi)存消耗的影響拧簸,應(yīng)當(dāng)避免使用過(guò)長(zhǎng)的鍵贾富。value對(duì)象更復(fù)雜些牺六,主要包含5種基本數(shù)據(jù)類(lèi)型:字符串、列表、哈希票灰、集合冯键、有序集合。其它數(shù)據(jù)類(lèi)型都是建立在這5種數(shù)據(jù)結(jié)構(gòu)之上實(shí)現(xiàn)的晓淀,如:Bitmaps和HyperLogLog使用字符串實(shí)現(xiàn),GEO使用有序集合實(shí)現(xiàn)等。每種value對(duì)象類(lèi)型根據(jù)使用規(guī)模不同,占用內(nèi)存不同冒萄。在使用時(shí)一定要合理預(yù)估并監(jiān)控vlaue對(duì)象占用情況,避免內(nèi)存溢出。

      2. 緩沖內(nèi)存

        緩沖內(nèi)存主要包括:客戶(hù)端緩沖、復(fù)制積壓緩沖區(qū)、AOF緩沖區(qū)咕幻。

        客戶(hù)端緩沖指的是所有接入到Redis服務(wù)器TCP連接的輸入輸出緩沖蓝厌。輸入緩沖無(wú)法控制隧膘,最大空間為1G,如果超過(guò)將斷開(kāi)連接。輸出緩沖通過(guò)參數(shù)client-output-buffer-limit控制,如下所示:

        • 普通客戶(hù)端:除了復(fù)制和訂閱的客戶(hù)端之外的所有連接锰镀,Redis的默認(rèn)配置是:client-output-buffer-limit normal 0 0 0氧腰,Redis并沒(méi)有對(duì)普通客戶(hù)端的輸出緩沖區(qū)做限制真友,一般普通客戶(hù)端的內(nèi)存消耗可以忽略不計(jì)挺尾,但是當(dāng)有大量滿連接客戶(hù)端接入時(shí)這部分內(nèi)存消耗就不能忽略了掂僵,可以設(shè)置maxclients做限制。特別是當(dāng)使用大量數(shù)據(jù)輸出的命令且數(shù)據(jù)無(wú)法及時(shí)推送給客戶(hù)端是舱卡,如monitor命令,容易造成Redis服務(wù)器內(nèi)存突然飆升既绩。

        • 從客戶(hù)端:主節(jié)點(diǎn)會(huì)為每個(gè)從節(jié)點(diǎn)單獨(dú)建立一條連接用于命令復(fù)制颜矿,默認(rèn)配置是:client-output-buffer-limit slave 256mb 64mb 60.當(dāng)主從節(jié)點(diǎn)之間網(wǎng)絡(luò)延遲較高或主節(jié)點(diǎn)掛載大量從節(jié)點(diǎn)時(shí)這部分內(nèi)存消耗將張勇很大一部分箍铭,建議主節(jié)點(diǎn)掛載的從節(jié)點(diǎn)不要多于2個(gè)拍摇,主從節(jié)點(diǎn)不要部署在較差的網(wǎng)絡(luò)環(huán)境下混卵,如異地跨機(jī)房環(huán)境,防止復(fù)制客戶(hù)端連接緩慢造成溢出踏拜。

        • 訂閱客戶(hù)端吧:當(dāng)使用發(fā)布訂閱功能時(shí),連接客戶(hù)端使用單獨(dú)的輸出緩沖區(qū)笋妥,默認(rèn)配置為:client-output-buffer-limit pubsub 32mb 8mb 60,當(dāng)訂閱服務(wù)的消息生產(chǎn)快于消費(fèi)速度時(shí)油挥,輸出緩沖區(qū)會(huì)產(chǎn)生積壓造成輸出緩沖區(qū)空間溢出惋鹅。

        輸入輸出緩沖區(qū)在大流量的場(chǎng)景中容易失控闰集,造成Redis內(nèi)存的不穩(wěn)定挚瘟,需要重點(diǎn)監(jiān)控。

        復(fù)制積壓緩沖區(qū):Redis在2.8版本之后提供了一個(gè)可重用的固定大小緩沖區(qū)用于實(shí)現(xiàn)部分復(fù)制功能泽谨,根據(jù)repl-backlog-size參數(shù)控制妒潭,默認(rèn)1MB。對(duì)于復(fù)制積壓緩沖區(qū)整個(gè)主節(jié)點(diǎn)只有一個(gè)揣钦,所有的從節(jié)點(diǎn)共享此緩沖區(qū)雳灾,因此可以設(shè)置較大的緩沖區(qū)空間,如100MB冯凹,這部分內(nèi)存投入是有價(jià)值的谎亩,可以有效避免全量復(fù)制。

        AOF緩沖區(qū):這部分空間用于在Redis重寫(xiě)期間保存最近的寫(xiě)入命令宇姚。AOF緩沖區(qū)空間消耗用戶(hù)無(wú)法控制匈庭,消耗的內(nèi)存取決于AOF重寫(xiě)時(shí)間和寫(xiě)入命令量,這部分空間占用通常很小浑劳。

      3. 內(nèi)存碎片

        Redis默認(rèn)的內(nèi)存分配器采用jemalloc阱持,可選的分配器還有:glibc、tcmalloc魔熏。內(nèi)存分配器為了更好地管理和重復(fù)利用內(nèi)存衷咽,分配內(nèi)存策略一般采用固定范圍的內(nèi)存塊進(jìn)行分配。例如jemalloc在64位系統(tǒng)中將內(nèi)存空間劃分為:小道逗、大兵罢、巨大三個(gè)范圍。每個(gè)范圍內(nèi)又劃分為多個(gè)小的內(nèi)存塊單位滓窍,如下所示:

        • 新舸省:[8byte], [16byte, 32byte, 48byte, ..., 128byte], [192byte, 256byte, ..., 512byte], [768byte, 1024byte, ..., 3840byte]

        • 大:[4KB, 8KB, 12KB, ..., 4072KB]

        • 巨大:[4MB, 8MB, 12MB, ...]

        比如當(dāng)保存5KB對(duì)象時(shí)jemalloc可能會(huì)采用8KB的塊存儲(chǔ),而剩下的3KB空間變?yōu)榱藘?nèi)存碎片不能再分配給其他對(duì)象存儲(chǔ)吏夯。內(nèi)存碎片雖然是所有內(nèi)存服務(wù)的通病此蜈,但是jemalloc針對(duì)碎片化問(wèn)題做了優(yōu)化,一般不會(huì)存在過(guò)度碎片化的問(wèn)題噪生,正常的碎片率(mem_fragmentation_ratio)在1.03左右裆赵。但是當(dāng)存儲(chǔ)的數(shù)據(jù)長(zhǎng)短差異較大時(shí),以下場(chǎng)景容易出現(xiàn)高內(nèi)存碎片問(wèn)題:

        • 頻繁做更新操作跺嗽,例如頻繁對(duì)已存在的鍵執(zhí)行append战授、setrange等更新操作。

        • 大量過(guò)期鍵刪除桨嫁,鍵對(duì)象過(guò)期刪除后植兰,釋放的空間無(wú)法得到充分利用,導(dǎo)致碎片率上升璃吧。

        出現(xiàn)高內(nèi)存碎片問(wèn)題是常見(jiàn)的解決方式如下:

        • 數(shù)據(jù)對(duì)齊:在條件允許的情況下盡量做數(shù)據(jù)對(duì)齊楣导,比如數(shù)據(jù)盡量采用數(shù)字類(lèi)型或者固定長(zhǎng)度字符串等,但是這要視具體的業(yè)務(wù)而定畜挨,有些場(chǎng)景無(wú)法做到筒繁。

        • 安全重啟:重啟節(jié)點(diǎn)可以做到內(nèi)存碎片重新整理噩凹,因此可以利用高可用架構(gòu),如Sentinel或Cluster毡咏,將碎片率過(guò)高的主節(jié)點(diǎn)轉(zhuǎn)換為從節(jié)點(diǎn)驮宴,進(jìn)行安全重啟。

  2. 子進(jìn)程內(nèi)存消耗

    子進(jìn)程內(nèi)存消耗主要指執(zhí)行AOF/RDB重寫(xiě)時(shí)Redis創(chuàng)建的子進(jìn)程內(nèi)存消耗血当。Redis執(zhí)行fork操作產(chǎn)生的子進(jìn)程內(nèi)存占用量對(duì)外表現(xiàn)為與父進(jìn)程相同幻赚,理論上需要一倍的物理內(nèi)存來(lái)完成重寫(xiě)操作禀忆。但Linux具有寫(xiě)時(shí)復(fù)制技術(shù)(copy-on-write)臊旭,父子進(jìn)程會(huì)共享相同的物理內(nèi)存頁(yè),當(dāng)父進(jìn)程處理寫(xiě)請(qǐng)求時(shí)會(huì)需要修改的頁(yè)復(fù)制出一份副本完成寫(xiě)操作箩退,而子進(jìn)程依然讀取fork時(shí)整個(gè)父進(jìn)程的內(nèi)存快照离熏。

    Linux Kernel在2.6.38內(nèi)核增加了Transparent Huge pages(THP)機(jī)制而有些Linux發(fā)行版及時(shí)內(nèi)核達(dá)不到2.6.38也會(huì)默認(rèn)加入并開(kāi)啟這個(gè)功能,如RedHat Enterprise Linux在6.0以上版本默認(rèn)會(huì)引入THP戴涝。雖然開(kāi)啟THP可以降低fork子進(jìn)程的速度滋戳,但之后copy-on-write期間復(fù)制內(nèi)存頁(yè)的單位從4KB變?yōu)?MB,如果父進(jìn)程有大量寫(xiě)命令啥刻,會(huì)加重內(nèi)存拷貝量奸鸯,從而造成過(guò)度內(nèi)存消耗。例如可帽,以下兩個(gè)執(zhí)行AOF重寫(xiě)時(shí)的內(nèi)存消耗日志:

    // 開(kāi)啟 THP:
    C * AOF rewrite: 1039 MB of memory used by copy-on-write
    // 關(guān)閉THP:
    C * AOF rewrite: 9 MB of memory used by copy-on-write
    

    這兩個(gè)日志出資同一Redis進(jìn)程娄涩,used_memory總量為1.5GB,子進(jìn)程執(zhí)行期間每秒寫(xiě)命令量都在200左右映跟。當(dāng)分別開(kāi)啟和關(guān)閉THP時(shí)蓄拣,子進(jìn)程內(nèi)存消耗有天壤之別。如果在高并發(fā)寫(xiě)的場(chǎng)景下開(kāi)啟THP努隙,子進(jìn)程內(nèi)存消耗可能是父進(jìn)程的數(shù)倍球恤,極易造成機(jī)器物理內(nèi)存溢出,從而觸發(fā)SWAP或OOM killer荸镊。

    子進(jìn)程內(nèi)存消耗總結(jié)如下:

    • Redis產(chǎn)生的子進(jìn)程并不需要消耗1倍的父進(jìn)程內(nèi)存咽斧,實(shí)際消耗根據(jù)期間寫(xiě)入命令量決定,但是依然要預(yù)留出一些內(nèi)存防止溢出躬存。

    • 需要設(shè)置sysctl vm.overcommit_memory=1 允許內(nèi)核可以分配所有的物理內(nèi)存张惹、防止Redis進(jìn)程執(zhí)行fork時(shí)因系統(tǒng)剩余內(nèi)存不足而失敗。

    • 排查當(dāng)前系統(tǒng)是否支持并開(kāi)啟THP优构,如果開(kāi)啟建議關(guān)閉诵叁,防止copy-on-write期間內(nèi)存過(guò)度消耗。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末钦椭,一起剝皮案震驚了整個(gè)濱河市拧额,隨后出現(xiàn)的幾起案子碑诉,更是在濱河造成了極大的恐慌,老刑警劉巖侥锦,帶你破解...
    沈念sama閱讀 222,590評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件进栽,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡恭垦,警方通過(guò)查閱死者的電腦和手機(jī)快毛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,157評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)番挺,“玉大人唠帝,你說(shuō)我怎么就攤上這事⌒兀” “怎么了襟衰?”我有些...
    開(kāi)封第一講書(shū)人閱讀 169,301評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)粪摘。 經(jīng)常有香客問(wèn)我瀑晒,道長(zhǎng),這世上最難降的妖魔是什么徘意? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 60,078評(píng)論 1 300
  • 正文 為了忘掉前任苔悦,我火速辦了婚禮,結(jié)果婚禮上椎咧,老公的妹妹穿的比我還像新娘玖详。我一直安慰自己,他們只是感情好邑退,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,082評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布竹宋。 她就那樣靜靜地躺著,像睡著了一般地技。 火紅的嫁衣襯著肌膚如雪蜈七。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,682評(píng)論 1 312
  • 那天莫矗,我揣著相機(jī)與錄音飒硅,去河邊找鬼。 笑死作谚,一個(gè)胖子當(dāng)著我的面吹牛三娩,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播妹懒,決...
    沈念sama閱讀 41,155評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼雀监,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起会前,我...
    開(kāi)封第一講書(shū)人閱讀 40,098評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤好乐,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后瓦宜,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體蔚万,經(jīng)...
    沈念sama閱讀 46,638評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,701評(píng)論 3 342
  • 正文 我和宋清朗相戀三年临庇,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了反璃。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,852評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡假夺,死狀恐怖淮蜈,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情侄泽,我是刑警寧澤礁芦,帶...
    沈念sama閱讀 36,520評(píng)論 5 351
  • 正文 年R本政府宣布蜻韭,位于F島的核電站悼尾,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏肖方。R本人自食惡果不足惜闺魏,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,181評(píng)論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望俯画。 院中可真熱鬧析桥,春花似錦、人聲如沸艰垂。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,674評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)猜憎。三九已至娩怎,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間胰柑,已是汗流浹背截亦。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,788評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留柬讨,地道東北人崩瓤。 一個(gè)月前我還...
    沈念sama閱讀 49,279評(píng)論 3 379
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像踩官,于是被迫代替她去往敵國(guó)和親却桶。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,851評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容

  • 前言 Redis是目前最火爆的內(nèi)存數(shù)據(jù)庫(kù)之一蔗牡,通過(guò)在內(nèi)存中讀寫(xiě)數(shù)據(jù)颖系,大大提高了讀寫(xiě)速度畴嘶,可以說(shuō)Redis是實(shí)現(xiàn)網(wǎng)站...
    小陳阿飛閱讀 806評(píng)論 0 1
  • 轉(zhuǎn)載:可能是目前最詳細(xì)的Redis內(nèi)存模型及應(yīng)用解讀 Redis是目前最火爆的內(nèi)存數(shù)據(jù)庫(kù)之一,通過(guò)在內(nèi)存中讀寫(xiě)數(shù)據(jù)...
    meng_philip123閱讀 1,442評(píng)論 1 22
  • 幾個(gè)常用了命令行 登錄 redis-cli -h 127.0.0.1 -p 6379 -a 123 查看內(nèi)存 in...
    lucode閱讀 995評(píng)論 0 2
  • 日本蜜月集晚,我們?nèi)チ撕脦讉€(gè)地方窗悯,而被熱推的京都自然不會(huì)錯(cuò)過(guò),只不過(guò)我們并沒(méi)有對(duì)那些古建筑有足夠的興趣偷拔,倒是好好體驗(yàn)了...
    在照片上跳霹靂舞閱讀 237評(píng)論 0 1
  • 我最近特別想念你 我發(fā)誓蒋院,我說(shuō)的是真的 你說(shuō) 把說(shuō)不出口的話 寫(xiě)成故事 贈(zèng)給黑夜與酒 贈(zèng)給星星與酒杯 唯獨(dú)沒(méi)有我 ...
    月半要爬坡閱讀 487評(píng)論 0 2