redis使用中存在的問題及如何避免(一)

帥帥的波發表於2018-04-09

redis可以滿足很多的應用場景,而且因為將所有資料都放到記憶體中,所以它的讀寫效能很好,很多公司都在使用redis。redis給我們帶來便利的同時,使用過程中會存在什麼問題呢,本文將簡單加以總結。

  • 阻塞問題
    redis使用了單執行緒來處理請求,為什麼單執行緒可以支援如此高的併發呢?主要有如下幾點:

    1. 純記憶體訪問:將所有資料都放到記憶體中,記憶體響應時間為100納秒,是redis達到每秒萬級別訪問的重要基礎
    2. 非阻塞IO:redis使用epoll作為I/O多路複用技術,redis自身的事件處理模型將epoll中的連線、讀寫、關閉都轉換為事件,不在網路I/O上浪費過多時間
    3. 單執行緒:避免了執行緒切換和競態產生的消耗,簡化了資料結構和演算法的實現
      因此如果某個命令執行時間過長,會造成其他命令阻塞,對redis來說是致命的

      產生阻塞的場景:
      A. API或資料結構使用不合理
         a. 避免使用某些易造成阻塞的命令如:keys sort hgetall smembers
            執行showlog get [n] 可以獲取最近n條執行慢的記錄,對於執行超過一定時間
            (預設10ms,線上建議設定為1ms)的命令都會記錄到一個定長佇列(預設128,可調整)中。              
         b. 防止一次操作獲取過多資料:縮減大物件或者把大物件拆分為多個小物件
            發現大物件的命令:redis-cli -h{ip} -p{port} bigkeys
            內部原理:採用分段進行scan操作,把歷史掃描過的大物件統計出來
         c. 防止大量key同時過期:如果有很多key在同一秒內過期,超過了所有key的25%,redis主執行緒就會阻塞直到過期key比例下降到25%以內,
            因此要避免同一時間過期大量key,過期時間可做雜湊處理。
            redis4.0引入的lazyfree機制可以避免del、flushdb、flushall、rename等命令引起的redis-server阻塞,提高服務穩定性。
           
      B. CPU飽和
         單執行緒的redis處理命令時只能使用一個CPU,CPU飽和是指redis把單核的CPU使用率跑到接近100%。
         首先要確定redis的併發量是否達到極限,通過redis-cli-h{ip} -p{port}--stat 獲取redis當前使用情況。
         如果達到每秒6w+左右的qps,說明單臺已跑到極限,需要水平擴充套件。
         如果qps只有幾百或者幾千CPU就已經飽和,可能使用了高演算法複雜度的命令或者是對記憶體的過度優化
         (如放寬了ziplist的使用條件,雖然使用的記憶體會變少,但是更耗CPU)。
            
      C. 持久化操作
         持久化引起主執行緒的阻塞操作主要有:fork阻塞、AOF刷盤阻塞、HugePage寫操作阻塞
         a. fork阻塞
            發生在RDB和AOF重寫時,redis主執行緒呼叫fork操作產生共享記憶體的子執行緒,由子執行緒完成持久化檔案的重寫工作,若fork操作耗時過長會引起阻塞。
            避免使用記憶體過大的例項。              
         b. AOF刷盤阻塞
            開啟AOF持久化功能時,一般會採用1次/s的刷盤方式,後臺執行緒每秒對AOF檔案做fsync操作,當硬碟壓力過大時fsync操作需要等待直到寫入完成。
            如果主執行緒距離上一次的fsync成功超過2s,為了資料安全會阻塞直到後臺執行緒執行完fsync完成。這種阻塞是由於磁碟壓力引起。
            儘量獨立部署
         c. HugePage寫操作阻塞
            子程式在執行重寫期間利用linux的copyonwrite機制,會拖慢寫操作的執行時間,導致大量寫操作慢查詢。
            優化linux配置
            
  • 快取穿透
    快取穿透是指查詢一個根本不存在的資料,快取層和儲存層都不命中,且不將空結果寫到快取中。
    會導致後端儲存負載變大,造成後端儲存當機等問題。可以在程式中分別統計總呼叫數、快取命中數、儲存命中數,若有大量儲存層空命中,可能是出現了快取穿透。
    產生原因:1.自身程式碼或資料出現問題 2.惡意攻擊,爬蟲造成空命中
    如何解決:

    1. 快取空物件
      儲存層不命中,扔將空物件儲存到快取層。
      適用場景:資料頻繁變化、實時性高
      帶來問題:
      a.快取了空值,會佔用記憶體空間;可以設定較短過期時間,自動剔除。
      b.資料不一致,若儲存層新增了此資料,有短暫不一致;可主動清除掉快取的空物件。
    2. 布隆過濾器
      在訪問快取層和資料層之前將存在的key用布隆過濾器提前儲存起來,做第一層攔截。
      適用場景:大使用者集,實時性要求較低的場景,如有幾億的資料集,每隔一段時間會新增使用者進去,在更新之前新使用者的訪問會存在快取穿透問題。
      缺點:程式碼維護複雜

相關文章