5分鐘學會這種更高效的Redis數據刪除方式

做者:馮偉源,騰訊雲數據庫架構師,騰訊雲Redis從零到一的技術運營負責人,第九屆中國數據庫技術大會分享嘉賓,擁有八年數據庫的運營開發與海量運維經驗,曾讓QQ與Qzone數據庫業務達到一人萬臺服務器的運營成熟度。redis

簡述

咱們知道,Del命令能刪除數據,除此以外,數據在Redis中,還會以哪一種方式被刪除呢?在Redis內存滿必定會返回OOM錯誤?Key到達過時時間就當即刪除?刪除大Key會影響性能嗎?下面,我們一塊兒探討。算法

同步和異步刪除

1.DEL 和 UNLINK

Redis服務自身對Key的刪除,能夠分爲「同步刪除」和「異步刪除」。使用DEL命令會觸發「同步刪除」,若是Key是一個有不少元素的複雜類型,這個過程可能會堵塞一下Redis服務自身,從而影響用戶的訪問。若是使用UNLINK命令,Redis服務會先計算刪除Key的成本,從而更智能地作出「同步刪除」或「異步刪除」的選擇。注意,只有4.0版本後,纔有UNLINK命令。

2.成本計算

那麼,成本是如何計算的呢?對於list,hash,set,zset的對象類型,若是長度大於64(由宏LAZYFREE_THRESHOLD定義),纔會採用異步刪除的手段,從當前db先釋放該key,再由另一個線程作異步刪除。對於長度不大於64的複雜類型,異步刪除比同步刪除還多了一些函數調用與多線程同步的代價,因此同步刪除更好。對於string對象,底層的數據結構sds是一份連續的內存,內存分配器回收這塊內存的複雜度是O(1),因此採用同步刪除也不會堵塞服務。

總的來講,咱們做爲用戶,都能用UNLINK替代DEL。

Key的驅逐

1.定義

Redis處理命令前根據內存容量是否觸達上限而進行的Key驅逐。

2.驅逐策略

Redis經過參數maxmemory來選擇不一樣的驅逐策略:
  • volatile-random 從已設置過時時間的數據集(server.db[i].expires)中任意選擇數據驅逐;
  • volatile-lru 從數據集(server.db[i].dict)中挑選最近最少使用的數據驅逐(2.8默認);
  • volatile-ttl 從已設置過時時間的數據集(server.db[i].expires)中尋找最近即將過時(ttl最小)的key來驅逐;
  • allkeys-random 從數據集(server.db[i].dict)中任意選擇數據驅逐;
  • allkeys-lru 從數據集(server.db[i].dict)中挑選最近最少使用的數據驅逐;
  • noeviction 禁止驅逐數據,永遠不驅逐,僅對寫操做返回一個錯誤(4.0默認);
在4.0版本後,還增長了如下兩種驅逐策略。
  • volatile-lfu在過時集合中使用LFU鏈來驅逐數據;
  • allkeys-lfu 從數據集(server.db[i].dict)使用LFU算法來驅逐數據;

3.簡述

Redis在處理命令前,會看看容量是否觸達上限。數據庫

若是驅逐策略爲noeviction,則不會驅逐Key,而是返回寫失敗。4.0後,在返回寫入失敗前,還會先檢測lazyfree線程是否還有待刪除的Key,沒有才會給用戶返回寫入失敗。

對於其餘策略,都會根據相應定義,進行Key的驅逐,這裏再也不詳述。

在4.0或以上的版本,Key的驅逐會基於參數lazyfree_lazy_eviction,來決定採用unlink仍是del。在2.8版本,則只會用del。lazyfree_lazy_eviction參數在Qcloud 4.0上是no。

這裏試問,主從節點都會進行「驅逐」麼?

答案是都會的,各自會因應自身的驅逐策略進行驅逐,而且Master節點驅逐的刪除命令還會傳播到Slave節點。

Key的訪問淘汰

1.定義

訪問一個已過時的Key會觸發對其的刪除。

2.簡述

與Key的驅逐同樣,Key的訪問淘汰一樣是基於訪問事件來觸發的。

主從角色的節點在處理訪問淘汰上的邏輯是不一樣的。

對於Slave節點,訪問到了已過時的Key,Slave節點會返回該Key不存在,但不會主動刪除該Key。刪除的動做,仍是會從Master上同步過來。

對於Master節點,在4.0或以上的版本,會根據參數lazyfree-lazy-expire,來決定用DEL仍是UNLINK。對於2.8版本,則只能用DEL了。這些刪除的動做,都會同步到Slave與AOF文件中。

在Qcloud 4.0以上的版本,默認是開啓異步刪除的,即lazyfree-lazy-expire=yes

Key的定時淘汰

1.定義

Redis自身的定時調度把已過時Key刪除。

2.簡述

多久會執行一次定時調度呢?

redis服務的參數hz能控制定時淘汰的頻率,hz默認是10,即每秒能調度100次。

剛纔說「訪問淘汰」的邏輯只會在Master角色上發生,那「訪問淘汰」也是嗎?

通常來講,Slave節點不會進行定時淘汰,它只會等待從Master節點同步過來的刪除命令,這樣就保持了主從之間的一致性。然而,有些時候,用戶會把Slave節點設置成可寫,那麼Slave上寫的帶有過時時間的Key,由於Master是不知道的,就一直不會淘汰掉。因此在版本4.0之後,Redis增長了單獨的邏輯,在定時淘汰中刪除這些在slave節點上寫入的過時Key。

對於Master節點,根據宏ACTIVE_EXPIRE_CYCLE_SLOW,能選擇兩種淘汰模式,分別是「FAST淘汰」和「SLOW淘汰」,前者每次淘汰只能花1毫秒,不能花更多了,後者是Qcloud默認的選項,這樣能在每次調度中淘汰更多的Key,但會花更多的CPU時間在淘汰上,下降了處理的訪問吞吐量。下面咱們針對「SLOW淘汰」展開描述。

SLOW淘汰模式,以hz=10爲例,每次調度的總時間是100ms,這裏調度不會25%的cpu時間,即25ms。

每淘汰多少個key,就檢測一次是否超25ms呢?

若是每淘汰1個Key就檢測一次,無疑代價太大。從源碼上看,定時淘汰會嘗試遍歷每一個db,遍歷完了或者時間到了就退出循環。第一層循環是遍歷各個db,第二層循環是遍歷db裏面的一批批key,一批key是20個,若是第三層循環結束後有大於5個key是成功淘汰的(說明這個db不少淘汰key),那麼二層就繼續循環,若是小於等於5個key,說明這個db沒有不少key須要淘汰,則退出二層循環,第三層循環是一批key裏面逐個key進行淘汰。即最多320個key進行判斷後,就會看看是否已經超過cpu佔用時間。

在4.0或以上的版本,會根據參數lazyfree-lazy-expire(默認no)來作DEL仍是UNLINK。在Qcloud的4.0以上版本,這裏會特地配置成yes,以便儘可能採用UNLINK操做。2.8版本不支持lazyfree-lazy-expire,就只能選擇DEL命令。

這裏的定時淘汰,也會以命令的形式,傳播到Slave節點與記錄到AOF文件中。

總結

1.驅逐策略的選擇,每每與業務特色、使用場景緊密相關。不當的選擇,可能會讓用戶丟失不想丟失的數據,或者致使較差的驅逐效率;服務器

2.已過時的Key每每不會馬上被刪除,用戶在導出快照與創建主從時,會疑惑主從之間的Key數量不一致,咱們都須要瞭解這一點;數據結構

3.驅逐與淘汰都有可能影響服務,在新版本下,最好都開啓unlink代替del。多線程

直播預告

特惠體驗雲數據庫

點擊享受騰訊雲Redis優惠活動架構

相關文章
相關標籤/搜索