30G 的redis 如何優化


突然發現我們的redis 已經用了30G了,好吧這是個很尷尬的數字因為我們的緩存機器的內存目前是32G的,內存已經告竭。幸好上上周公司采購了90G的機器,現在已經零時遷移到其中的一台機器上了。(跑題下,90G的內存太爽了是我除了koding.com 之外第二次用到90G的機器,koding 是個好網站,在線編程IDE。) 但是隨着數據量越來越大單機始終無法承受的,改造勢在必行。經過初步思考我們得出了很簡單的方案 概括起來就是    "內外兼修"

1.內功修煉

先從我們的應用層說起 看看redis 使用情況 ,有沒有辦法回收一些key ,先進入redis 服務器執行 info ,有刪減

   1:  redis 127.0.0.1:6391> info
   2:  used_memory_human:35.58G   
   3:  keyspace_hits:2580207188 
   4:  db0:keys=2706740,expires=1440700

目前我們只使用了1個DB 但是key 太多了 有270W個key,已經過期的有144W。第一個想到的就是我勒個去,怎么會有這么多key ,第二個想法就是可能存在過大的key

看看能不能針對過大的key 做優化?可是遺憾的是官方並沒有命令顯示db 的key 大小,我們只能自己想辦法了

Google 一番,發現國外友人已經寫好了shell

傳送門: https://gist.github.com/epicserve/5699837

可以列出每個key 大小了。可是這並不適用我們,因為我們key 太大了 執行了9個小時都沒跑完,無力吐槽了。 其實還有一個選擇就是用另外一個工具

傳送門:https://github.com/sripathikrishnan/redis-rdb-tools

可惜這個太重了 ,不想麻煩ops ,我們就只能撩起袖子,造輪子。

把shell 代碼簡單看了下發件DEBUG OBJECT 是個好東西啊 ,google 下發現官網 http://redis.io/commands/object

已經有簡單的調試信息了,剩下的就好處理了

   1: #coding=utf-8
   2: import redis
   3:  
   4: COLOR_RED = "\033[31;49;1m %s \033[31;49;0m"
   5:  
   6: COLOR_GREED = "\033[32;49;1m %s \033[39;49;0m"
   7:  
   8: COLOR_YELLOW = "\033[33;49;1m %s \033[33;49;0m"
   9:  
  10: COLOR_BLUE = "\033[34;49;1m %s \033[34;49;0m"
  11:  
  12: COLOR_PINK = "\033[35;49;1m %s \033[35;49;0m"
  13:  
  14: COLOR_GREENBLUE = "\033[36;49;1m %s \033[36;49;0m"
  15:  
  16:  
  17: def getHumanSize(value):
  18:     gb = 1024 * 1024 * 1024.0
  19:     mb = 1024 * 1024.0
  20:     kb = 1024.0
  21:     if value >= gb:
  22:         return COLOR_RED % (str(round(value / gb, 2)) + " gb")
  23:     elif value >= mb:
  24:         return COLOR_YELLOW % (str(round(value / mb, 2)) + " mb")
  25:     elif value >= kb:
  26:         return COLOR_BLUE % (str(round(value / kb, 2)) + " kb")
  27:     else:
  28:         return COLOR_GREED % (str(value) + "b")
  29:  
  30:  
  31: month = 3600 * 24 * 30
  32: result = []
  33: client = redis.Redis(host="XXXXX", port=XXXX)
 
           
          
  36: client.info()
  37:  
  38: count = 0
  39: for key in client.keys('*'):
  40:     try:
  41:         count += 1
  42:         idleTime = client.object('idletime', key)
  43:         refcount = client.object('refcount', key)
  44:         length = client.debug_object(key)['serializedlength']
  45:         value = idleTime * refcount
  46:         print "%s key :%s , idletime : %s,refcount :%s, length : %s , humSize  :%s" % (count, key, idleTime, refcount, length, getHumanSize(length))
  47:     except Exception:
  48:         pass

寫了個簡單的python 腳本輸出每個key 的大小和idle time,和refer count 。有了這么多數據結合awk 就可以很好的統計每個key 的使用情況。有一點要注意的是這個size 是key 在redis 中的大小,並非實際的大小,這個是經過redis 壓縮的。經過分析之后發現不存在過大的key ,但是存在有些key 半年都沒有被訪問過 Orz 。

接下來就很好處理了,我們為每個key 設置的過期時間,若key 被hit 上則更新這個expire time 。這樣可以逐步淘汰冷數據,達到冷熱分離

 

2. 外功修煉

我們對內清理了無效的key,對外我們要做到水平擴展,單機的承載始終有限,於是我們開始了傳說中的分布式改造

分布式這東西看起來很唬人做起來更唬人,幸好我們是緩存服務 CAP約束有限。 緩存服務做分布式最好的當然是一致性hash 咯。其實當我們改造完成之后,才發現官方已經准備做這個分布式的緩存體系了(流口水啊) 只是現在還在開發中 給了個備用的響當當的  Twemproxy  奈何我們已經做好了,就先用着,坐等官方測試之后再說

傳送門: http://redis.io/topics/cluster-spec

我們實現了數據的平滑遷移,而且對server 的修改實現了最小影響。 因為原來是用的是phpredis 所以就擴展了下,代碼可以平滑過渡。

我們自己的實現:https://github.com/trigged/redis_con_hash

其實扯了這么多就是要把redis 的數據分散開,單機的承載始終是個瓶頸,但是redis 在這方面沒有Memcached 完善,不過以后會越來越好


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM