大規模的數據庫存儲系統中,數據的生命週期管理是頗有必要的;從業務角度發現過時數據,數據歸檔和數據碎片整理等。以MySQL爲例,1個運行好久的TB級MySQL實例中,極有可能數百GB的數據,對業務來講是」過時數據」可直接歸檔後清理。若是不能發現和及時清理,這部分「過時數據」對生產數據庫備份資源消耗,佔用工做集數據內存(過時數據行可能分散InnoDB的page中),影響數據還原的RTO等。從成本和運維的角度看,代價都是很大的。針對MySQL這類」過時數據」問題,經過MySQL巡檢系統發現問題,使用MySQL歸檔系統備份和刪除數據等。 node
本文簡單聊下Redis」死鍵」的問題,從業務角度對」死鍵」的2個定義:redis
Redis可對鍵設置生存時間, 當鍵的生存時間爲0(過時鍵)理論就會被刪除,並釋放佔用的數據結構和內存資源。shell
但Redis爲保證請求的性能,過時鍵並非當即刪除的。數據庫
這節主要討論,當產生過時鍵的速度>>Redis刪除過時鍵的速度時,致使過時鍵堆積的問題。緩存
Redis刪除過時鍵有兩種策略:passive way和active way.服務器
咱們重點討論第二種」按期刪除策略」。Redis每一個database(Cluster模式下只有0號庫)都對應expire的dict,用以保存Redis設置有生存時間的鍵;Redis每秒調用10次(hz參數決定)activeExpireCycle函數;session
Redis按期刪除過時鍵的速度? 怎麼監控它?數據結構
Redis按期刪除動做每秒執行10次,正常狀況每次刪除幾個過時鍵,這樣每秒刪除過時鍵約數十個。 經過info stats的expired_keys指標記錄累計刪除的過時鍵數量。根據生產監控(hz=10)Redis每秒刪除過時鍵20~25個,天天能刪除約200百萬個過時鍵。有的Redis單個實例包含數千萬個鍵,若是業務設計鍵過時處理不合理,天天產生過時鍵多於200百萬。這容易致使Redis實例中存在過時鍵,最壞狀況佔整個鍵容量的25%;也就說Redis實例最壞有1/4的內存被這類過時的」死鍵」所佔據浪費。併發
Redis 查看過時鍵刪除數量 127.0.0.1:xxx> info stats # Stats total_connections_received:33843364 total_commands_processed:211474375292 instantaneous_ops_per_sec:9438 total_net_input_bytes:19661370696457 total_net_output_bytes:34509115216581 expired_keys:7575307675 evicted_keys:0 keyspace_hits:72743876832 keyspace_misses:57604962586 latest_fork_usec:95143
大量過時鍵堆積,最直接影響是浪費內存空間;另外還會有些」靈異現象」運維
這些現象都和過時鍵的堆積有關。那麼咱們怎麼避免這類過時鍵堆積呢。
有效避免Redis過時鍵堆積,從兩個方面解決: 下降過時鍵產生的速度;和加快按期刪除的速度。
如下是一個shell, 獲取當前服務器,Cluser的Master經過scan方式清理過時鍵 local_ip=`ifconfig | grep -Eo 'inet (addr:)?([0-9]*\.){3}[0-9]*' | grep -Eo '([0-9]*\.){3}[0-9]*' | grep -v '127.0.0.1'` redis-cli -p 6379 cluster nodes | grep "master" | grep "$local_ip" | while read node do node_ins=`echo $node | awk '{print $2}' | cut -f 1 -d ":" ` node_port=`echo $node | awk '{print $2}' | cut -f 2 -d ":" ` redis-cli -h $node_ins -p $node_port --scan >> /dev/null done
業務低峯期,找個Redis Master實例,支持scan命令(QPS會增加1w),查看命令執行先後,dbsize/used_memory是否有明顯降低 redis-cli -h $node_ins -p $node_port –scan >> /dev/null
一個Redis集羣,分析鍵空間發現70%的鍵,3個月未訪問過。這類鍵沒未設置生存時間,實例也不能設置淘汰機制。 不少應用程序功能已下線,但它使用的Redis鍵每每無人清理或經過DBA處理;這樣的鍵從業務角度看,屬於無用的」死鍵」。
每一個Redis鍵都有一個lru的屬性字段,用於記錄它最後一次被訪問的時間。
而object idletime命令,可經過系統當前時間-lru時間,獲得鍵多久沒有被訪問的秒數。
說明:object idletime命令訪問鍵時,不會改變鍵的lru屬性,即不會影響鍵的訪問時間
如下示例,鍵"key:000000008149"已有150039秒未被訪問過 127.0.0.1:7000> object idletime "key:000000008149" (integer) 150039 127.0.0.1:7000> object idletime "key:000000008149" (integer) 150041
使用Python寫個簡單程序,scan指定數據庫的鍵空間,打印idletime超過指定時閥值的鍵。
#-*- coding:utf8 -* import redis import time //Action: scan 0號數據庫的鍵空間,獲取空閒時長大於指定時間的鍵的列表,達到獲取業務死鍵的做用 //日期: 2016-08-11 TIME_THRESHOLD_SECOND = 2592000 # 獲取idletime時長超過TIME_THRESHOLD_SEC秒數鍵打印. 默認:30天 COUNT = 200 #scan每次返回的鍵個數,建議不要太大,避免O(n)的n過大出現慢查詢. 默認:200個 YEILD_SECOND = 0.05 #每次scan後,sleep 0.05秒;本地測試若是不sleep,此工具會增長約2w的QPS. 避免對高負載的Redis實例產生影響。 #默認:0.05秒,增加約3500個QPS,其中一個時間複雜度是O(COUNT). 若是實例負載高,key很少能夠考慮sleep 0.1秒 def get_key_idletime(): r = redis.StrictRedis(host='127.0.0.1', port=6380, password="xxxx" ,db=0) cursor = '0' while cursor != 0: cursor, data = r.scan(cursor=cursor, count=COUNT) for key in data: key_idletime = r.object("idletime",key) if key_idletime > TIME_THRESHOLD_SECOND: print key , " ", key_idletime time.sleep(YEILD_SECOND) get_key_idletime()
咱們定位Redis的長期未被訪問的鍵,咱們怎麼確認屬於哪一個業務功能呢? 怎麼預防業務的「死鍵」存在?
每一個團隊按大業務功能有多個集羣,每一個集羣有多個小功能模塊;這樣命空間管理後,集羣有任何問題,DBA定位致使問題的」鍵前綴」,經過集羣對接負責的工程師 很快就定位是哪一個功能,什麼狀況引發的問題。