大廠Redis性能優化指南

1 客戶端優化

pipeline 批量操做

鏈接池應用

2 合理的淘汰機制

  • 設置合理的內存大小
  • 設置合理的過時時間
  • 選擇合適的淘汰策略

3 內存優化

redis.io/topics/memo… 不一樣大小,底層數據結構不一樣:node

hash-max-ziplist-value 64 
zset-max-ziplist-value 64
複製代碼

4 CPU優化

不要阻塞,特別是 lua 腳本,不要有長時間睡眠操做,否則其它操做所有阻塞! 謹慎使用範圍操做 SLOWLOG get 10 默認10ms,默認只保留最後的128條python

5 key設計

可讀性和可管理性

以業務名(或數據庫名)爲前綴(防止key衝突),用冒號分隔,好比業務名:表名:idredis

簡潔性

保證語義的前提下,控制key的長度,當key較多時,內存佔用也不容忽視數據庫

不要含特殊字符

好比:包含空格、換行、單雙引號以及其餘轉義字符緩存

6 value設計

  • 拒絕bigkey(防止網卡流量、慢查詢), string類型控制在10KB之內, hash、 list. set. zset元素 個數不要超過5000
  • 選擇適合的數據類型
  • 控制key的生命週期,redis不是垃圾桶

7 分區

多個業務系統,共用一個redis,仍是應該分開。規劃好key,特別是前綴,a.b.c.dbash

若是緩存數據變大了,就能夠分區,但注意按業務垂直拆分,避免 key 的衝突。 通常小公司研發團隊都是須要申請緩存資源直接獲得對應的 ip 地址,但大公司最多隻能獲得key/token。markdown

8 Big Key

那麼什麼樣的Key纔算是Big Key呢? 通常key的值大於10KB時能夠算是Big Key了。 以下場景都有可能遇到Big Key,好比:網絡

  • 粉絲列表
  • 統計數據,好比PV或者UV統計
  • 使用不當的數據緩存,好比經過String保存序列化後的用戶數據等

出現了Big Key你也不用很是緊張,由於某些場景下不得不使用,而不該該是你的使用不當形成的。下面咱們看一下如何進行發現與優化數據結構

查詢 Big Key

可使用腳本進行查詢,大概思路就是使用 scan 遊標查詢 key,而後使用 memory usage key 獲取這個 key 與 value 的字節數,這樣就能很方便的得出結論進行優化。好比異步

import sys
import redis

def check_big_key(r, k):
  bigKey = False
  length = 0 
  try:
    type = r.type(k)
    if type == "string":
      length = r.strlen(k)
    elif type == "hash":
      length = r.hlen(k)
    elif type == "list":
      length = r.llen(k)
    elif type == "set":
      length = r.scard(k)
    elif type == "zset":
      length = r.zcard(k)
  except:
    return
  if length > 10240:
    bigKey = True
  if bigKey :
    print db,k,type,length

def find_big_key_normal(db_host, db_port, db_password, db_num):
  r = redis.StrictRedis(host=db_host, port=db_port, password=db_password, db=db_num)
  for k in r.scan_iter(count=1000):
    check_big_key(r, k)

def find_big_key_sharding(db_host, db_port, db_password, db_num, nodecount):
  r = redis.StrictRedis(host=db_host, port=db_port, password=db_password, db=db_num)
  cursor = 0
  for node in range(0, nodecount) :
    while True:
      iscan = r.execute_command("iscan",str(node), str(cursor), "count", "1000")
      for k in iscan[1]:
        check_big_key(r, k)
      cursor = iscan[0]
      print cursor, db, node, len(iscan[1])
      if cursor == "0":
        break;
  
if __name__ == '__main__':
  if len(sys.argv) != 4:
     print 'Usage: python ', sys.argv[0], ' host port password '
     exit(1)
  db_host = sys.argv[1]
  db_port = sys.argv[2]
  db_password = sys.argv[3]
  r = redis.StrictRedis(host=db_host, port=int(db_port), password=db_password)
  nodecount = r.info()['nodecount']
  keyspace_info = r.info("keyspace")
  for db in keyspace_info:
    print 'check ', db, ' ', keyspace_info[db]
    if nodecount > 1:
      find_big_key_sharding(db_host, db_port, db_password, db.replace("db",""), nodecount)
    else:
      find_big_key_normal(db_host, db_port, db_password, db.replace("db", ""))
複製代碼

能夠經過 python find_bigkey host 6379 password 來執行,默認大 key 的閾值爲 10240(10KB),也就是對於 string 類型的 value 大於 10240 的認爲是大 key,對於 list 的話若是 list 長度大於 10240 認爲是大 key,對於 hash 的話若是 field 的數目大於 10240 認爲是大 key。另外默認該腳本每次搜索 1000 個 key,對業務的影響比較低,不過最好在業務低峯期進行操做,避免 scan 命令對業務的影響。

刪除 Big Key

Redis4.0 新特性 - Lazy Free 當刪除鍵的時候, redis 提供異步延時釋放 key 內存的功能,把 key 釋放操做放在 bio(Background I/O) 單獨的子線程處理中,減小刪除 big key 對 redis 主線程的阻塞。有效地避免刪除 big key 帶來的性能和可用性問題。所以刪除 Big Key 時使用 unlink 操做。

9 複製

9.1 避免全量複製

Redis 複製分爲全量複製和部分複製,但全量複製的開銷很大。如何儘可能避免全量複製呢?

解決方案

  • 當某個 Replica 第一次去掛到 master 上時,沒法避免要作一次全量複製,那又該如何儘可能下降開銷呢?

既然第一次沒法避免,那就選在集羣低峯時間(凌晨)掛載 Replica

  • 選舉Replica爲主節點

若是有某個Replica已經有了主節點數據了,那就將其選舉爲 master。經過人爲或者哨兵實現選舉。

複製積壓緩衝區不足

master 生成 RDB 同步到 Replica,Replica 加載 RDB 這段時間裏,master 的全部寫命令都會保存到一個複製緩衝隊列(若是主從之間網絡抖動,進行部分複製也是執行該邏輯),待 Replica 加載完 RDB,拿 offset 值到該隊列判斷,若在該隊列,則把該隊列從 offset 到末尾所有同步過來,這個隊列的默認值爲 1M。若發現 offset 不在隊列,就會發起全量複製。 因此增大複製緩衝區的配置 repl_backlog_size 默認是 1M,能夠設置大一些,從而增大 offset 的命中率。那麼如何估算該值呢?通常咱們網絡故障時間通常是min級別,因此以下公式:

理想值 = 根據當前QPS估算每分鐘可寫入多少字節 ✖️ 可能發生故障的min
複製代碼

9.2 避免複製風暴

9.2.1 單主節點複製風暴

通常發生在 Master 掛在多個 Replica 場景。 當 Master 重啓恢復後,其 master 下全部 Replica 檢測到 RunID 變化,致使全部 Replica 向 master 作全量複製。此時 Master 會爲 Replica 建立 RDB 快照,若在快照建立完畢前,有多個 Replica 嘗試與 Master 進行全量同步,那麼其餘的 Replica 將共享這份 RDB 快照。儘管 Redis 對這個問題作了優化,即只生成一份 RDB,但仍需屢次傳輸,開銷很大。同時給多個 Replica 發送快照,可能會使 Master 的網絡帶寬消耗嚴重,形成 Master 延遲變大,極端狀況會出現主從斷開,致使複製失敗。

解決方案

  • 採用樹狀複製結構(一傳二,二傳四,分攤網絡壓力)
  • 當 Master 宕機,選舉一臺 Replica 晉升爲 Master,減小全量複製的產生

9.2.2 單機多主複製風暴

一臺機器,多個主節點(樹狀複製結構)。 由於Redis是單線程結構,因此發揮不了 CPU多核的優點,因而有的人爲了省錢就在同一機器部署多個 redis 主節點了。

解決方案

  • 應當把主節點儘可能分散在多臺機器上,避免在單臺機器上部署過多的主節點
  • 當主節點所在機器故障後提供故障恢復轉移機制,避免機器恢復後進行密集的全量複製

禁止使用

使用經驗

性能

  • 線程數(4~8)與鏈接數(redis 服務端 10000)
  • 監控系統讀寫比( 至少9:1)和緩存命中率

容量

  1. 作好容量評估,合理使用緩存資源

資源管理和分配

  • 儘可能每一個業務集羣單獨使用本身的Redis,不混用;

  • 控制Redis資源的申請與使用,規範環境和Key的管理(以一線互聯網爲例);

  • 監控CPU 100%(單線程),因此此時基本不響應了,須要優化高延遲操做。

相關文章
相關標籤/搜索