Redis Note(五)性能優(yōu)化

(五)redis性能問題

A. redis客戶端

  • redis客戶端通信
    • redis新版本對于網(wǎng)絡請求進行多線程處理,收到請求后redis實際處理數(shù)據(jù)依然為單線程模式双谆。
    • redis客戶端同服務端間的通信基于tcp協(xié)議返干。
  • jedis客戶端
    • 開發(fā)階段將jedis的jar包加入project中兴枯,或使用maven添加依賴。
    • jedis直連模式
      • 是默認方式矩欠,但每次使用都會新建tcp連接财剖。
      • jedis直連簡單方便,但存在對象線程不安全的問題癌淮,適用于少量連接的情況躺坟。
    • jedisPool模式
      • 生產(chǎn)多采用該方式,預先生成幾個jedis對象于pool中该默,這些對象保持長連接狀態(tài)瞳氓。
      • 具體使用時,用getJedisFromPool方法獲取對象栓袖,再完成后續(xù)實際操作匣摘。
      • 向jedisPool借用jedis對象是本地操作,并無網(wǎng)絡開銷裹刮∫舭瘢控制pool的大小可以有效確定并發(fā)的壓力,遠小于新建tcp的方式捧弃。

B. redis內(nèi)存理解

  • 概述:redis所有實時使用的數(shù)據(jù)都存儲于內(nèi)存中赠叼,持久化文件用于備份恢復。本小節(jié)九內(nèi)存消耗违霞、內(nèi)存管理 & 優(yōu)化進行闡述分析嘴办。

1. 內(nèi)存消耗

  • 內(nèi)存使用統(tǒng)計

    • info memory:查看內(nèi)存相關指標,重點需要關注的是used_memory_rss, used_memory & 它們的比值mem_fragmentation_ratio买鸽。

    • 閾值注意點

      • mem_fragmentation_ratio > 1時涧郊,說明redis占用OS的內(nèi)存并非全是數(shù)據(jù)存儲消耗的,是內(nèi)存碎片消耗的眼五,兩者相差很大則說明碎片率很嚴重妆艘;
      • mem_fragmentation_ratio < 1時,說明內(nèi)存不足時OS進行swap看幼,讓硬盤臨時替代成內(nèi)存給redis使用批旺,這導致redis性能急劇下降
    • 核心指標說明

      屬性名 屬性說明
      used_memory_human used_memory可讀顯示
      used_memory_rss 操作系統(tǒng)下诵姜,redis進程占用的物理內(nèi)存總量
      mem_fragmentation_ratio used_memory_rss / used_memory汽煮,大于1就有碎片;小于1則說明有swap給redis用
  • 內(nèi)存消耗分類

    • 自身內(nèi)存:redis空進程自身消耗內(nèi)存很少棚唆,通常used_memory_rss在3M左右暇赤,used_memory則約800kb。
    • 對象內(nèi)存:最主要消耗內(nèi)存的部分瑟俭,存儲用戶所有數(shù)據(jù)翎卓。一個對象由key和value兩部分組成。
    • 緩沖內(nèi)存:緩沖內(nèi)存包括客戶端緩沖摆寄、復制積壓緩沖區(qū) & AOF緩沖區(qū)失暴。在客戶端連接數(shù)過大時,可能會造成redis內(nèi)存飆升微饥。
    • 內(nèi)存碎片:redis默認內(nèi)存分配器是jemalloc逗扒,一般采用固定范圍的內(nèi)存塊分配,將內(nèi)存分為小欠橘、大矩肩、巨大三個范圍。頻繁的更新操作(append肃续、setrange等)黍檩、大量過期key刪除易導致碎片出現(xiàn)叉袍。建議重啟節(jié)點完成碎片整理。
  • 子進程內(nèi)存消耗

    • 場景:在AOF或RDB時刽酱,fork的子進程理論上會需要和父進程一樣的內(nèi)存空間喳逛。基于linux的copy-on-write技術棵里,父子進程可共享物理內(nèi)存頁润文。但在接受寫請求時,父進程仍然需要對修改內(nèi)容所在的內(nèi)存頁進行復制以完成寫操作殿怜。
    • THP:transparent huge pages機制在linux2.6.38后出現(xiàn)典蝌,默認開啟,導致復制內(nèi)存頁從4kb變成2mb头谜。若有大量寫請求骏掀,這會導致內(nèi)存消耗急劇上升。

2. 內(nèi)存管理

  • 設置內(nèi)存上限目的:防止所用內(nèi)存超出服務器物理內(nèi)存乔夯,一般情況下設置合理的maxmemory砖织,保證機器預留20 - 30%閑置內(nèi)存。

  • 動態(tài)調(diào)整內(nèi)存上限:config set maxmemory 10g & config rewrite末荐。注意侧纯,一旦使用內(nèi)存上限調(diào)整就會激活內(nèi)存回收

  • 內(nèi)存回收策略

    • 過期對象回收:精準維護每個key過期會消耗大量cpu資源甲脏,對單線程的redis來說成本過高眶熬,一般有兩種模式解決。

      1. 惰性刪除:客戶端讀取到expired的key時块请,會執(zhí)行刪除操作并返回空值娜氏。該方式雖然節(jié)約cpu,但存在內(nèi)存泄漏問題墩新。若expired的key一直未被讀取就不會被刪除贸弥,內(nèi)存無法釋放。
      2. 定時任務刪除:redis內(nèi)部維護一個定時任務海渊,默認每秒運行10次绵疲,通過配置hz完成。默認使用慢模式運行臣疑,每個數(shù)據(jù)庫空間隨機找20個key檢查盔憨,發(fā)現(xiàn)過期時刪除對應的key-value。若本次檢查中超過25%的key過期讯沈,則繼續(xù)檢查20個key郁岩,直到低于25%或者運行超時才結束回收的循環(huán)。
    • 內(nèi)存溢出控制策略:基于mexmemory-policy進行策略的選取 & 確認,可使用config set maxmemory-policy xxx進行動態(tài)配置问慎。

      策略名 策略具體內(nèi)容
      noeviction 默認策略萍摊,不刪除任何數(shù)據(jù),拒絕所有寫入操作并向客戶端返回OOM錯誤信息蝴乔。
      volatile-lru 根據(jù)LRU算法刪除expired的key记餐,直到空間足夠為止驮樊。若沒有可刪除對象薇正,則回退成noeviction策略。
      allkeys-lru 根據(jù)LRU刪除key囚衔,不管有無設置超時屬性挖腰。
      allkeys-random 隨機刪除所有key。
      volatile-random 隨機刪除expired的key练湿。
      volatile-ttl 根據(jù)key-value的ttl屬性猴仑,刪除最近將要過期的數(shù)據(jù),若沒有復合的對象回退noeviction策略肥哎。

4. 內(nèi)存優(yōu)化

  • 概述:redis所有存儲數(shù)據(jù)都是用redisObject封裝辽俗,有下方幾個字段
字段 含義
type 當前對象數(shù)據(jù)類型,主要為string篡诽,hash崖飘,list,set & zset杈女。注意朱浴,key都是string類型。
encoding redis內(nèi)部編碼類型达椰,代表其內(nèi)部采用的數(shù)據(jù)結構翰蠢。
lru 記錄對象最后一次被訪問的時間。
refcount 記錄當前對象被引用的次數(shù)啰劲,若refcount=0就可以被安全回收梁沧。
*ptr 和對象的數(shù)據(jù)內(nèi)容相關,若為整數(shù)直接存儲數(shù)據(jù)蝇裤;若非整數(shù)則表示指向數(shù)據(jù)的指針廷支。
  • 縮減對象:縮減key和value的長度是最直接的方法。
  • 共享對象池:redis內(nèi)部維護0-9999的整數(shù)對象池猖辫,可通過object refcount查看引用次數(shù)驗證是否啟動整數(shù)對象池技術酥泞。
    • 效果:使用該技術后,數(shù)據(jù)內(nèi)存使用率可降低30%以上啃憎。
    • 限制:使用maxmemory-policy中的lru策略時(無論allkeys或volatile)芝囤,redis禁止使用共享對象池。因為共享時,lru字段也需要共享悯姊,導致無法獲取每個對象最后的訪問時間羡藐。
  • 其他:字符串的優(yōu)化(預分配機制、字符串重構)悯许,encoding類型優(yōu)化 & 控制key數(shù)量仆嗦。

C. redis緩存設計

  • 概述
    • 緩存收益
      • 加速讀寫。
      • 降低后端負載壓力先壕,例如減少數(shù)據(jù)庫訪問量和復雜計算瘩扼。
    • 成本代價
      • 數(shù)據(jù)不一致性:緩存層和存儲層數(shù)據(jù)在一定時間窗口內(nèi)有數(shù)據(jù)不一致問題,需要存儲層更新至緩存層垃僚。
      • 代碼維護成本:新增對于緩存層的代碼 & 緩存與存儲邏輯的代碼集绰。
      • 運維成本增加,維護集群狀態(tài)谆棺。

1. 緩存更新策略

  • LRU栽燕、LFU或FIFO算法
    • 使用場景:key數(shù)量過多,消耗內(nèi)存達到設置的maxmemory閾值時改淑,會對key-value的剔除碍岔。
    • 問題:產(chǎn)生一致性問題較嚴重,刪除后導致緩存層數(shù)據(jù)缺失且運維人員無法及時得知刪除信息朵夏。
    • 配置方法:設置maxmemory大小 & maxmemory-policy算法即可蔼啦。
  • 超時剔除
    • 使用場景:緩存數(shù)據(jù)配置了expire命令,保障key-value在一段時間后失效刪除侍郭。
    • 問題:在一定窗口內(nèi)有一致性問題询吴。
  • 主動更新
    • 使用場景:對數(shù)據(jù)一致性要求較高時使用,若真實數(shù)據(jù)更新立即更新緩存層亮元。
    • 問題:維護成本較高猛计,需開發(fā)正完成更新邏輯。
  • 案例實踐建議
    • 低一致性業(yè)務:使用最大內(nèi)存淘汰算法即可爆捞。
    • 高一致性業(yè)務:超時剔除+主動更新奉瘤,結合使用。

2. redis雪崩

  • 現(xiàn)象:redis的大量的key在某個時間段失效redis服務不可用煮甥,導致需要直接訪問數(shù)據(jù)庫盗温,大量請求沖擊database。
  • 解決方案
    • 不要設置固定過期時間成肘,盡量在setex時采用隨機的ttl卖局。
    • 保證緩存層服務高可用性,基于分布式集群架構双霍。

3. redis緩存穿透

  • 現(xiàn)象:redis和database都不存在該key對應的數(shù)據(jù)砚偶,但用戶不斷發(fā)起對該類數(shù)據(jù)的請求導致database掛掉批销,比如持續(xù)大量請求uid的-1的對應數(shù)據(jù)。
  • 解決方案
    • 緩存空對象染坯,后續(xù)再訪問該key可基于緩存層返回null值均芽,存在問題是導致緩存了更多的無用key & 數(shù)據(jù)不一致問題。該策略適用于數(shù)據(jù)命中不高单鹿,數(shù)據(jù)頻繁變化實時性高的場景掀宋。
    • 設置database的并發(fā)鎖,防止大量請求在database上進行仲锄。
    • 設置攔截器劲妙,類似bitmapsbloom filter,不存在的uid直接攔截在redis層昼窗,不可達database是趴。適用于數(shù)據(jù)命中不高,數(shù)據(jù)相對固定的場景澄惊。

4. redis擊穿

  • 現(xiàn)象:redis中沒有該key但是database中有,一般出現(xiàn)場景為某個hot key過期富雅,大量用戶并發(fā)查詢該key導致直接沖擊database掸驱。
  • 解決方案
    • hot key重建
      • 定義:緩存失效的瞬間,有大量線程來重建緩存没佑。
      • 負面效應:重建時后段附在加大毕贼,所以需要減少重建緩存的次數(shù)。
    • 設置互斥鎖
      • 互斥鎖通過競爭對資源獨占使用蛤奢,執(zhí)行順序是亂序的鬼癣。該方法利用setex或setnx實現(xiàn),確保只有一個線程重建緩存啤贩,其他線程都在等待狀態(tài)待秃。
      • 同步鎖更高級,能保證執(zhí)行順序痹屹。
    • 設置hot key永不過期
      • 直接不設置過期時間章郁,或者設置一個邏輯過期時間,超過該周期后單獨使用一個線程重建緩存志衍。
      • 但容易出現(xiàn)數(shù)據(jù)一致性問題暖庄。

D. pipeline & transaction

  • pipeline
    • 概述:管道技術是客戶端行為對redis server透明楼肪,server不知道發(fā)過來的請求是普通的還是pipeline形式的培廓。
    • 使用效果:類似批量的方式缺失降低了網(wǎng)絡開銷,利用多個命令一次網(wǎng)絡發(fā)送提升了效率春叫〖缒疲基于hgetall的操作并沒有對應的mhgetall可用俘侠,此時pipeline可派上用場。
  • transaction
    • 概述:事務是redis server行為蔬将,當客戶端使用了MULTI命令后把客戶端對象設置為特殊狀態(tài)爷速。客戶端發(fā)出的命令會被緩存到server霞怀,等到EXEC再執(zhí)行惫东。
    • 使用效果:客戶端在exec前的命令,都會被server返回一個queued毙石,事務模式能提高網(wǎng)絡使用效率廉沮,但不支持回滾

E. 異常排查 & 配置優(yōu)化

1. Jedis客戶端常見異常

  • 無法從連接池獲取到連接:jedisPool的jedis個數(shù)有限(默認8個)徐矩,若他們都被使用時滞时,那么第9個來調(diào)用的就會報錯。

    • 報錯信息:redis.clients.jedis.exceptions.JedisConnectionException: Could not get get a resource from the pool
    • 高并發(fā)場景下滤灯,連接池的jedis個數(shù)可進行調(diào)整坪稽。若我們認為一個jedis每條命令處理時間5ms,1s單個jedis對象極限能處理200個命令鳞骤,8個jedis的qps極限約為200*8=1600窒百。若qps預計在1.6w的話,需將jedis數(shù)量調(diào)整至80豫尽。
    • 若存在慢查詢篙梢,那么這個jedis也會被長時間占用不歸還,可能造成該異常美旧。
  • 讀寫超時

    • 報錯信息:redis.clients.jedis.exceptions.JedisConnectionException & java.net.SocketTimeoutException: read timed out
    • 讀寫超時設置的太短渤滞。
    • 讀寫操作耗時太久,需優(yōu)化具體邏輯榴嗅。
  • 連接超時

    • 報錯信息:redis.clients.jedis.exceptions.JedisConnectionException & java.net.SocketTimeoutException: connect timed out
    • 連接超時設置太短妄呕,修改參數(shù)jedis.getClients().setConnectionTimeout(A larger value)
    • redis發(fā)生阻塞录肯,tcp-backlog滿了導致新連接失敗趴腋。
    • 服務端與客戶端間的網(wǎng)絡存在問題。
  • 客戶端緩沖區(qū)異常

    • 報錯信息:redis.clients.jedis.exceptions.JedisConnectionException: Unexpected end of stream
    • jedis輸出的緩沖區(qū)大小偏小论咏,若set bigkey优炬,可能就會報錯.
    • jedis不正常并發(fā)讀寫,被多個線程操作厅贪。
  • 其他場景異常

    • lua腳本正在執(zhí)行:正在運行l(wèi)ua腳本時會報錯蠢护。

    • 加載持久化文件:正在加載rdb文件時會報錯。

    • 內(nèi)存使用情況超出maxmemory:若redis使用內(nèi)存過多养涮,超出預設的maxmemory會報錯OOM葵硕,需要擴容調(diào)整眉抬。

    • 客戶端連接數(shù)過大:超出預設的maxclients時,會報錯懈凹,具體解決手段有兩個大方向

      1. 客戶端:若maxclients設置的閾值不小蜀变,則一般是客戶端使用不當導致的,可下線部分應用節(jié)點先降低總連接數(shù)介评。

      2. 服務端:基于服務端一般是高可用模式库北,可采用故障轉移機制,下線有問題的redis節(jié)點们陆,完成主從切換后再排查寒瓦。

  • 實際案例

    • redis內(nèi)存陡增:master內(nèi)存狂增幾乎打滿maxmemory,但slave內(nèi)存幾乎無變化坪仇,導致無法寫入新數(shù)據(jù)杂腰,報出OOM的錯誤。
      • 確有大量key寫入:基于slave內(nèi)存無變化的現(xiàn)象椅文,可初步判定主從間復制出現(xiàn)問題喂很,比較兩側dbsize進行復核。
      • 其他原因:造成master壓力大可能是jedis等客戶端緩沖區(qū)導致的雾袱,比如客戶端使用monitor命令恤筛,可使用info clients確認。
    • 客戶端周期性超時:redis服務端無明顯異常芹橡,可發(fā)現(xiàn)部分慢查詢。
      • 網(wǎng)絡原因?qū)е略摤F(xiàn)象出現(xiàn)望伦。
      • redis服務端自身問題林说,需查log文件再確認。
      • 查詢慢查詢時間點屯伞,查看客戶端日志中是否存在redis.clients.jedis.exceptions.JedisConnectionException & java.net.SocketTimeoutException: connect timed out的錯誤信息腿箩。

2. 運維配置優(yōu)化

a. Linux - 內(nèi)存分配控制overcommit
  • 定義:Linux對于大部分申請內(nèi)存的請求回復為yes,便于運行更多的程序劣摇。大多數(shù)情況下珠移,申請完內(nèi)存后并不會馬上使用內(nèi)存,該技術即為overcommit末融。本處內(nèi)存代表钧惧,物理內(nèi)存+swap之和

  • 參數(shù)具體可選值

    含義
    0 表示內(nèi)核將檢查是否有足夠的可用內(nèi)存勾习,若有足夠內(nèi)存浓瞪,則申請通過;若沒有足夠的可用內(nèi)存巧婶,則申請失敗乾颁。
    1 表示內(nèi)核允許超量使用內(nèi)存直到耗盡涂乌。
    2 表示內(nèi)核絕不過量使用內(nèi)存(never overcommit),系統(tǒng)整個內(nèi)存地址空間不超過swap+50%RAM值英岭。
  • 常見場景:redis啟動時湾盒,日志中出現(xiàn)WARNING overcommit is set to 0!...

  • redis中的解決方案:上述日志提示修改vm.overcommit_memory=1,便于持久化操作的fork能在低內(nèi)存下執(zhí)行成功诅妹。

    • 獲取參數(shù):cat /proc/sys/vm/overcommit_memory
    • 設置參數(shù):echo "vm.overcommit_memory=1" >> /etc/sysctl.conf罚勾,并進行sysctl vm.overcommit_memory=1
b. Linux - 內(nèi)存交換swappiness
  • 定義:物理內(nèi)存不足時將一部分內(nèi)存頁進行swap操作——將硬盤暫時作內(nèi)存使用。若遇到高并發(fā)漾唉、高吞吐的應用荧库,此時磁盤IO會成為系統(tǒng)瓶頸。在Linux 下赵刑,并不是物理內(nèi)存使用完才會使用swap分衫,具體何時使用swap基于swappiness參數(shù)。

  • 參數(shù)含義:swappiness取值范圍在 0 -100間般此,默認值為60蚪战,該值越大意味著操作系統(tǒng)使用swap的概率越高。

    策略
    0 Linux3.5及更新版本铐懊,寧愿OOM killer也不用swap邀桑。
    1 Linux3.5及更新版本,寧愿用swap也不用OOM killer科乎。
    60 默認值壁畸。
    100 Linux會主動使用swap。
  • 設置方法

    • 配置swappiness:使用echo {bestvalue} > /proc/sys/vm/swapiness
    • 確保重啟依然生效:使用echo vm.swappiness={bestvalue} >> /etc/sysctl.conf
  • 常見場景

    • redis在物理內(nèi)存充足時運行極快茅茂,物理內(nèi)存不足時該配置可避免redis死掉捏萍。
    • 若redis時高可用的情況下,寧愿死掉也不要使用swap空闲,因為這會導致阻塞令杈。
    • 除了直接free -h查看swap情況,亦可參考mem_fragmentation_ratio碴倾,若 < 1則存在redis使用swap的情況逗噩。
c. Linux - THP
  • 定義:Linux kernel在2.6.38后增加了THP特性,默認開啟跌榔。全稱Transparent Huge Pages异雁,支持大內(nèi)存頁2MB分配,可加快fork子進程的速度矫户,但fork操作后每個內(nèi)存頁從4KB增加到2MB片迅,會大幅度增加重寫期間父進程的內(nèi)存消耗,每次寫命令引起的復制內(nèi)存頁單位放大了512倍皆辽。
  • 參數(shù)設置
    • 禁用THP特性:使用命令echo never > /sys/kernel/mm/transparent_hugepage/enabled
    • 確保重啟依然生效:編輯開機配置文件/etc/rc.local柑蛇,追加禁用THP特性命令——echo never > /sys/kernel/mm/transparent_hugepage/enabled
  • 常見場景
    • redis啟動時日志中出現(xiàn)WARNING you have Transparent Huge Pages (THP) support enabled in your kernel...芥挣,需手動禁用THP。
    • 部分版本的Linux 未把THP放入/sys/kernel/mm/transparent_hugepage/enabled耻台,例如Red Hat 6以上的版本即將THP配置放入/sys/kernel/mm/redhat_transparent_hugepage/enabled空免,但redis將檢查THP的位置寫死導致不能成功檢測THP問題并將之暴露于日志中,但THP的問題依舊存在需要手動調(diào)整盆耽。
d. Linux - OOM Killer
  • 定義:在內(nèi)存不足時選擇性地殺掉用戶進程蹋砚,OOM Killer會為每個用戶進程設置一個權值,該值越高越容易被優(yōu)先干掉摄杂。
  • 參數(shù)設置
    • 參數(shù)位置:每個進程的權值位于/proc/{pid}/oom_score中坝咐,受到/proc/{pid}/oom_adj的控制。
    • oom_adj
      • 當oom_adj設置為最小值時析恢,該進程不會被干掉墨坚。不同Linux 版本的最小值不同,可參考Linux 源碼中的oom.h(-15到-17)映挂。
      • 設置方法為echo {value} > /proc/{pid}/oom_adj
  • 適用場景:為保證redis在主機內(nèi)存使用率接近滿載時仍然能存活下來泽篮,可將redis進程對應的oom_adj設置成最小值。
e. Linux - 其他參數(shù)
  • NTP

    • 定義:網(wǎng)絡時間協(xié)議柑船,全稱network time protocol帽撑,保證不同機器的時鐘一致性。
    • 場景:redis集群有多節(jié)點組成鞍时,涉及多主機問題亏拉。時鐘不一致會導致redis集群內(nèi)問題排查難度提升,例如無法有效判別clutser故障轉移逆巍。
    • 解決手段:建議每小時使用一次時鐘同步专筷,保障其集群內(nèi)時鐘一致性。具體為0 * * * * /usr/sbin/ntpdate ntp.xx.com > /dev/null 2>&1
  • ulimit

    • 定義:查看和設置系統(tǒng)當前用戶進程的資源數(shù)蒸苇,常用命令ulimit -a,其展示效果可見下方吮旅。

      category unit value
      core file size (blocks, -c) 0
      data seg size (kbytes, -d) unlimited
      scheduling priority (-e) 0
      file size (blocks, -f) unlimited
      pending signals (-i) 61370
      open files (-n) 4096
      POSIX message queues (bytes, -q) 819200
      max user processes (-u) 61370
    • 場景

      • redis允許同時多個客戶端通過網(wǎng)絡進行連接溪烤,可通過服務端配置maxclients參數(shù)來進行具體客戶端數(shù)的限制。

      • redis除了供給客戶端連接數(shù)所需的10000FD句柄庇勃,還需要32個FD供redis自身內(nèi)部使用檬嘀。

      • Linux系統(tǒng)下德崭,所有網(wǎng)絡連接都是文件句柄绊序,若當前open files為4096谆奥,則啟動redis時會有下方日志

        You requested maxclients of 10000 requiring at least 10032 max file descriptors...Current maximum open files is 4096...

    • 解決手段:將open files的值設置為65535劝赔,通過命令ulimit -Sn 65535實現(xiàn)达布。

  • TCP backlog

    • 定義
      • backlog是一個連接隊列,在Linux內(nèi)核2.2之前创淡,backlog包括半連接狀態(tài)和全連接狀態(tài)兩種隊列大刑汛;在Linux內(nèi)核2.2之后衷掷,分離為兩個backlog來分別限制半連接(SYN_RCVD狀態(tài))隊列大小和全連接(ESTABLISHED狀態(tài))隊列大小辱姨。
      • 半連接狀態(tài):服務器處于Listen狀態(tài)時收到客戶端SYN報文時放入半連接隊列中,即SYN queue(服務器端口狀態(tài)為:SYN_RCVD)戚嗅。
      • 全連接狀態(tài):TCP的連接狀態(tài)從服務器(SYN+ACK)響應客戶端后雨涛,到客戶端的ACK報文到達服務器之前,則一直保留在半連接狀態(tài)中懦胞;當服務器接收到客戶端的ACK報文后替久,該條目將從半連接隊列搬到全連接隊列尾部,即 accept queue (服務器端口狀態(tài)為:ESTABLISHED)躏尉。
    • 場景:redis默認tcp-backlog值為511蚯根,若Linux系統(tǒng)的值小于511則會在啟動日志中出現(xiàn)WARNING: THE TCP backlog setting of 511 cannot be ...
    • 解決手段
      • 查看系統(tǒng)值:通過命令cat /proc/sys/net/core/somaxconn查看accept queue具體值,redis不涉及修改syn queue的問題醇份,若想查看可通過cat /proc/sys/net/ipv4/tcp_max_syn_backlog進行查詢稼锅。
      • 修改:使用echo 511 > /proc/sys/net/core/somaxconn進行變更。
f. 數(shù)據(jù)恢復手段
  • AOF機制恢復
    • 場景:若誤操作flushall或flushdb導致數(shù)據(jù)丟失僚纷,需借助持久化文件進行恢復矩距。若開啟了appendonly yes,則誤操作僅在AOF文件中追加了一條操作記錄怖竭。
    • 解決手段
      • 若AOF文件重寫了锥债,則之前的數(shù)據(jù)就無法順利找回,所以需要調(diào)整auto-aof-rewrite-percentage & auto-aof-rewrite-min-size阻止AOF自動重寫痊臭,并拒絕手動bgrewriteaof哮肚。
      • 確保上方參數(shù)設置后,將AOF文件中的flush操作去掉广匙,并確保AOF文件格式正常以保障數(shù)據(jù)的順利恢復允趟。
  • RDB機制恢復
    • 前置配置:若rdb持久化設置中有自動策略,例如save 900 1這類鸦致,則因為flush一般涉及key value都較多潮剪,會導致RDB恢復基本無望。除非并無開啟rdb自動策略分唾,否則面對flush誤操作抗碰,rdb不能有效恢復數(shù)據(jù)。
    • 解決手段:若redis沒有進行rdb自動策略绽乔,僅有手動的bgsave所得的rdb文件弧蝇,那么即可使用rdb文件恢復數(shù)據(jù),但相對數(shù)據(jù)完整性不如aof機制。

F. redis阻塞問題

  • 概述
    • redis屬于典型的單線程架構看疗,讀寫操作皆于唯一的主線程中完成沙峻。
    • redis處于高并發(fā)場景時,主線程若出現(xiàn)阻塞則問題嚴重鹃觉,阻塞時jedis會拋出JedisConnectionException異常专酗。
    • 阻塞內(nèi)因:不合理使用api或數(shù)據(jù)結構,cpu飽和盗扇、持久化阻塞(basave的fork瞬間或save持久化)祷肯。
    • 阻塞外因:cpu競爭、內(nèi)存交換(swap)疗隶、網(wǎng)絡問題佑笋。

1. 內(nèi)因詳解

a. api或數(shù)據(jù)結構使用不合理
  • 場景案例:redis正常場景下執(zhí)行命令速度極快(微妙級),若執(zhí)行hgetall命令且該hash類型的key是big key斑鼻,則執(zhí)行速度就會很慢蒋纬,屬于是典型的不合理使用api & 數(shù)據(jù)結構。

  • 慢查詢

    • 定義:默認配置slowlog-log-slower-than 10000坚弱,即10ms以上的操作被判定為慢查詢蜀备,slowlog-max-len 128默認存儲慢查詢?nèi)罩鹃L度為128條。
    • 獲取結果:slowlog get 10荒叶,即獲得最近的10條慢查詢命令碾阁。
  • 解決方案

    • 定制慢查詢閾值:線上一般可設置1ms為慢查詢閾值,即slowlog-log-slower-than 1000些楣,該速度下單機redis的qps將被限制為1000左右脂凶。

    • 拓展慢查詢?nèi)罩鹃L度:增加日志長度幫助排查故障的原因,將slowlog-log-slower-than可設置為1000愁茁。

    • 其他:使用快速命令hgetall改成hmget蚕钦,并禁用keys、sort命令鹅很,并將big key拆分嘶居。

  • 大鍵big key問題

    • 定義
      • big key指value所占空間很大的key,一般地促煮,string類型的key食听,若其value大于10KB即為big key。其他類型的big key污茵,是因為包含的元素過多。
      • 一個string類型的key葬项,value最大為512MB泞当;一個list類型的key,其value最多可存儲(2的32次方-1)個元素民珍。
    • 危害
      • 內(nèi)存空間不均:若在redis集群模式下襟士,bigkey必須被分配至某個節(jié)點盗飒,導致內(nèi)存空間使用不均勻。
      • 阻塞:因為操作big key容易耗時過高陋桂,導致阻塞時間偏長逆趣。
      • 網(wǎng)絡擁塞:每次獲取big key產(chǎn)生的網(wǎng)絡流量較大,占用帶寬嗜历。
    • 檢測big key
      • 被動收集:big key在被訪問時宣渗,出現(xiàn)異常日志。通過這種情況探測出的big key梨州,即為被動收集的模式痕囱。
      • 主動檢測:使用redis-cli -h xxx.xxx.xxx.xxx -p 6379 --bigkeys探測,其本質(zhì)是使用scan漸進式遍歷暴匠,rdr也是很好的redis分析工具鞍恢,在從節(jié)點上操作時更合適的手段。
    • 刪除big key
      • string類型:該類的big key一般可以直接del key即可每窖,不會阻塞帮掉。
      • hash、list窒典、set & sorted set:直接刪除依然會導致阻塞蟆炊,若為hash類型的場景下,可結合hscan逐步獲取進行刪除崇败。
      • Redis4.0后特性:使用lazy delete free模式盅称,刪除big key不會阻塞。
  • 熱鍵hot key問題

    • 常見場景:熱點突發(fā)新聞后室、熱銷大賣商品會給系統(tǒng)帶來巨大流量缩膝,在集群模式下存儲這些信息的redis節(jié)點會出現(xiàn)流量不均勻的情況,存儲熱點消息的節(jié)點出現(xiàn)QPS壓力偏大的問題岸霹。
    • 判別方法
      • 代理端:若客戶端對redis服務端的請求疾层,是基于Twemproxy/Codis這樣的分布式架構,則所有的請求都通過代理端再達到服務端贡避,可在代理處完成數(shù)據(jù)統(tǒng)計分析big key痛黎。
      • 服務端:使用monitor在server處分析客戶端大量請求中的hot key,但是使用monitor會影響redis性能刮吧,且該方法是針對redis單節(jié)點進行的湖饱,若為集群模式還需要聚合統(tǒng)計。
    • 解決方案
      • 拆分復雜數(shù)據(jù)結構:若為二級結構(list杀捻、hash等)井厌,等可拆分成若干key-value,分散至redis集群的各個節(jié)點上。
      • 遷移:將hot key所在的slot單獨放置到一個redis節(jié)點仅仆。
      • 本地緩存:將hot key放在業(yè)務端的本地緩存中器赞,處理速度更快且降低redis壓力,但需要注意數(shù)據(jù)一致性問題墓拜。
b. cpu飽和
  • 簡述:redis處理命令時為單線程港柜,僅有主線程處理業(yè)務,所以可用cpu數(shù)為1咳榜。若單核cpu的主機夏醉,則使用率接近100%,可使用top命令或redis-cli -h xxx.xxx.xxx.xxx -p 6379 --stat查看使用情況贿衍,stat中connections是歷史總連接數(shù)累計授舟,實時數(shù)據(jù)可看lsof -i:6379 | wc -l
  • 解決方案
    1. 若看到qps已經(jīng)足夠高(一般可達6w)贸辈,那么此時垂直優(yōu)化較困難释树,建議通過集群化完成水平擴展。
    2. 若發(fā)現(xiàn)qps僅數(shù)千甚至更低擎淤,那么大概率使用了高時間復雜度的命令奢啥,需修改業(yè)務方法。
    3. 內(nèi)存優(yōu)化過度嘴拢,可用info commandstats檢查桩盲,比如hset耗時過長,為節(jié)約內(nèi)存空間放寬ziplist的使用條件席吴,可修改hash-max-ziplist-entries & hash-max-ziplist-values赌结。
c. 持久化導致堵塞
  • 前置說明:不考慮使用save的場景,這種模式會導致長時間redis堵塞孝冒。

  • fork阻塞:rdb和aof重寫恢復數(shù)據(jù)時柬姚,主線程會fork生成子進程,子進程再完成實際的重寫庄涡。若fork耗時過長量承,則可能導致阻塞出現(xiàn);

  • aof阻塞:開啟aof后穴店,文件刷盤的方式一般為1s一次撕捍,后臺線程對aof文件進行fsync操作,當磁盤壓力大時fsync會需要等待泣洞,其主要緣由是磁盤壓力偏大忧风。

2. 外因詳解

  • cpu競爭
    • 進程競爭:redis屬于cpu密集型應用,雖然一般情況下cpu不是性能瓶頸球凰,但是不建議和其他服務部署在一臺主機上阀蒂。
    • 綁定cpu:redis為盡可能地充分利用多核cpu该窗,通常會一臺機器部署多個redis實例。此場景下蚤霞,不同redis進程綁定對應的cpu可降低上下文切換的開銷,但是某個實例進行rdb或者aof持久化時义钉,可能因為綁定cpu會導致壓力過大昧绣。
  • 內(nèi)存交換swap
    • 簡述:swap空間即內(nèi)存空間不足時,將磁盤充當內(nèi)存使用捶闸。
    • 甄別手段
      1. 找到redis pid夜畴,通過redis-cli -p 6379 info server | grep process_id
      2. 接著根據(jù)id查詢swap信息删壮,使用cat /proc/xxxx/smaps | grep swap贪绘。正常情況下應該都是0kb或者少量4kb。
  • 網(wǎng)絡問題
    • 拒絕連接:網(wǎng)絡割接或帶寬耗盡時的網(wǎng)絡閃斷(sar -n DEV查看)央碟,redis超過默認1w的maxclients連接數(shù)税灌,或者是進程限制打開文件數(shù)(ulimit -n)、backlog隊列溢出亿虽。
    • 網(wǎng)絡延遲:客戶端到redis服務器之間網(wǎng)絡延遲菱涤,可使用redis-cli -h xxx.xxx.xxx.xxx -p 6379 --latency核驗。
    • 網(wǎng)卡軟中斷:高并發(fā)下單個網(wǎng)卡隊列只能使用一個cpu洛勉,新版redis已經(jīng)改進網(wǎng)絡方面效率問題粘秆。
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市收毫,隨后出現(xiàn)的幾起案子攻走,更是在濱河造成了極大的恐慌,老刑警劉巖此再,帶你破解...
    沈念sama閱讀 216,544評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件昔搂,死亡現(xiàn)場離奇詭異,居然都是意外死亡引润,警方通過查閱死者的電腦和手機巩趁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來淳附,“玉大人议慰,你說我怎么就攤上這事∨铮” “怎么了别凹?”我有些...
    開封第一講書人閱讀 162,764評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長洽糟。 經(jīng)常有香客問我炉菲,道長堕战,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,193評論 1 292
  • 正文 為了忘掉前任拍霜,我火速辦了婚禮嘱丢,結果婚禮上,老公的妹妹穿的比我還像新娘祠饺。我一直安慰自己越驻,他們只是感情好,可當我...
    茶點故事閱讀 67,216評論 6 388
  • 文/花漫 我一把揭開白布道偷。 她就那樣靜靜地躺著缀旁,像睡著了一般。 火紅的嫁衣襯著肌膚如雪勺鸦。 梳的紋絲不亂的頭發(fā)上并巍,一...
    開封第一講書人閱讀 51,182評論 1 299
  • 那天,我揣著相機與錄音换途,去河邊找鬼懊渡。 笑死,一個胖子當著我的面吹牛怀跛,可吹牛的內(nèi)容都是我干的距贷。 我是一名探鬼主播,決...
    沈念sama閱讀 40,063評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼吻谋,長吁一口氣:“原來是場噩夢啊……” “哼忠蝗!你這毒婦竟也來了?” 一聲冷哼從身側響起漓拾,我...
    開封第一講書人閱讀 38,917評論 0 274
  • 序言:老撾萬榮一對情侶失蹤阁最,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后骇两,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體速种,經(jīng)...
    沈念sama閱讀 45,329評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,543評論 2 332
  • 正文 我和宋清朗相戀三年低千,在試婚紗的時候發(fā)現(xiàn)自己被綠了配阵。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,722評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡示血,死狀恐怖棋傍,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情难审,我是刑警寧澤瘫拣,帶...
    沈念sama閱讀 35,425評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站告喊,受9級特大地震影響麸拄,放射性物質(zhì)發(fā)生泄漏派昧。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,019評論 3 326
  • 文/蒙蒙 一拢切、第九天 我趴在偏房一處隱蔽的房頂上張望蒂萎。 院中可真熱鬧,春花似錦淮椰、人聲如沸岖是。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至烈疚,卻和暖如春黔牵,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背爷肝。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評論 1 269
  • 我被黑心中介騙來泰國打工猾浦, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人灯抛。 一個月前我還...
    沈念sama閱讀 47,729評論 2 368
  • 正文 我出身青樓金赦,卻偏偏與公主長得像,于是被迫代替她去往敵國和親对嚼。 傳聞我的和親對象是個殘疾皇子夹抗,可洞房花燭夜當晚...
    茶點故事閱讀 44,614評論 2 353

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