刪除大key時要小心
問題
redis大key是讓人比較頭疼的問題,如果線上redis出現大key,斷然不可立即執行del
,因為大key的刪除會造成阻塞。阻塞期間,所有請求都可能造成超時,當超時越來越多,新的請求不斷進來,這樣會造成redis連線池耗盡,盡而引發線上各種依賴redis的業務出現異常。
做個簡單測試
透過指令碼先向redis寫入大量的資料:
127.0.0.1:6379> hlen hset_test (integer) 3784945
這裡看到大概有300多萬的資料,我們執行個del
看看:
127.0.0.1:6379> del hset_test (integer) 1 (3.90s)
可以發現耗時將近4s
。
我們知道redis核心是單執行緒在跑的,那麼這個阻塞期間,redis是無法處理其他請求的。
低峰期刪除
最簡單的方式就是在業務低峰期進行刪除,比如大部分場景在凌晨4點左右比較低峰,這時候執行刪除,造成的影響比較小。當然這種方式也是無法避免阻塞期間的請求,一般適用執行期間qps非常小的業務。
scan分批
既然大key不能一下刪除,那麼我們就分批刪除。
hset
對於hset,我們hsan分批刪除。
# 虛擬碼 HSCAN key 0 COUNT 100 HDEL key fields
每次取個100條,然後刪除
set
對於set,我們可以每次隨機取一批資料,然後刪除
# 虛擬碼 SRANDMEMBER key 10 SREM key fields
zset
對於zset,每次可以直接刪除一批資料
虛擬碼 ZREMRANGEBYRANK key 0 10
list
對於list,直接pop
虛擬碼
i:=0
for {
lpop key
i++
if i%100 == 0 {
sleep(1ms)
}
}
非同步刪除
過期key刪除策略
有人說既然線上刪除大key會造成阻塞,那麼就對這個key設定一個TTL,交給redis自己去刪。我先看看redis的過期key刪除策略:
定期刪除:
我們知道redis的key分為帶過期的和永久的,對於有過期時間的key,redis會單獨放在一個字典表裡,單獨的好處就是redis知道這個字典裡的key隨時可能過期,那麼我就定期過來處理下,定期的任務就交給了serveCron,預設每100ms執行一次。每當serveCron執行的時候,就會去帶ttl的key裡面隨機抽取一部分key來檢查,如果這批key真的過期了,那麼就執行同步刪除。隨機抽查的原因:
不可能全部檢驗的,阻塞執行緒
隨機的話體現一定的公平性
惰性刪除
透過定期刪除,我們可以每次刪除一批已經過期的key,但是如果一個key已經過期了,定期刪除也沒清理到,這時使用者來讀取這個key的話,肯定不能直接返回,這時也會檢查這個key是否過期,如果過期直接刪除,返回空。
淘汰策略
noeviction:當記憶體使用超過配置的時候會返回錯誤,不會驅逐任何鍵
allkeys-lru:透過LRU演算法驅逐最久沒有使用的鍵
volatile-lru:透過LRU演算法從設定了過期時間的鍵集合中驅逐最久沒有使用的鍵
allkeys-random:從所有key中隨機刪除
volatile-random:從過期鍵的集合中隨機驅逐
volatile-ttl:從配置了過期時間的鍵中驅逐馬上就要過期的鍵
volatile-lfu:從所有配置了過期時間的鍵中驅逐使用頻率最少的鍵
allkeys-lfu:從所有鍵中驅逐使用頻率最少的鍵
淘汰策略是一個靈活的選項,一般根據業務來選擇合適的淘汰策略,那麼自定義的淘汰策略是何時觸發的?當然是我們進行加key或者更新一個更大的key的時候。所以他的刪除也是同步的,如果正好淘汰一個大key的時候,很不幸當前也會發生阻塞。
總結:不管以上三種哪個觸發的刪除,它都是同步的。所以就算加個TTL,redis也是同步刪除的,大key還是會造成阻塞。
非同步刪除
在redis4.0的時候,作者對於大key刪除造成阻塞的問題也做了考慮,於是出現了非同步刪除,非同步刪除也分為使用者主動和程式被動。
主動刪除
unlink
對於主動刪除,redis提供了del
的替代方法unlink
,當我們在unlink的時候,redis會先檢查要刪除元素的個數(比如集合),如果集合的元素的小於等於64個的時候,就會直接執行同步刪除,因為這不算一個大key,不會浪費很多的開銷,但是當超過64個的時候,redis會認為是大key的機率比較大,這時候redis會在字典裡,先把key刪除,真正的value會交給非同步執行緒來操作,這樣的話就不會對主執行緒造成任何影響。
flushall、flushdb
在執行flushall或者flushdb的時候,增加了ASYNC選項 FLUSHALL [ASYNC]
,當使用者沒設定ASYNC的時候,此時的flush操作是阻塞的,當設定了ASYNC的時候,會建立一個新的空字典,然後指向它,老字典交給非同步執行緒來慢慢刪。
被動刪除
redis配置策略
lazyfree-lazy-eviction:針對redis有設定記憶體達到maxmemory的淘汰策略時,這時候會啟動非同步刪除,此場景非同步刪除的缺點就是如果刪除不及時,記憶體不能得到及時釋放。
lazyfree-lazy-expire:對於有ttl的key,在被redis清理的時候,不執行同步刪除,加入非同步執行緒來刪除。
replica-lazy-flush:在slave節點加入進來的時候,會執行flush清空自己的資料,如果flush耗時較久,那麼複製緩衝區堆積的資料就越多,後面slave同步資料較相對慢,開啟replica-lazy-flush後,slave的flush可以交由非同步現成來處理,從而提高同步的速度。
lazyfree-lazy-server-del:這個選項是針對一些指令,比如rename一個欄位的時候
RENAME key newkey
, 如果這時newkey是存在的,對於rename來說它就要刪除這個newkey的value,如果這個newkey是一個大key,那麼就會造成阻塞,當開啟了這個選項時也會交給非同步執行緒來操作,這樣就不會阻塞主執行緒了。
題外話:rename
先來做個測試:
127.0.0.1:6379> set A 1 OK 127.0.0.1:6379> eval "for i=1,10000000,1 do redis.call('hset','B', i,1) end" 0 (15.89s)
設定A為1
向B裡面新增1000w的資料
B肯定是大key了,這時想把A重新命名成B執行rename A B
127.0.0.1:6379> rename A B OK (11.07s)
發現阻塞了,這是因為redis刪除B造成的,如果有rename的場景一定要注意newkey是否已經存在,newkey是否是大key。
作者:假裝懂程式設計
連結:
來源:掘金
著作權歸作者所有。商業轉載請聯絡作者獲得授權,非商業轉載請註明出處。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/756/viewspace-2797389/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Redis刪除大KeyRedis
- 【Redis】 redis-cluster刪除指定的keyRedis
- Redis 可以根據訊息儲存時長 將key 刪除嗎Redis
- 面試官:Redis中大Key怎麼刪除?面試Redis
- 不小心刪除/etc/passwd檔案怎麼辦
- Redis 實用小技巧——批次刪除指定的 keyRedis
- Redis刪除特定字首key的優雅實現Redis
- Redis 刪除1.2億指定字首的keyRedis
- win10 刪除登入介面的賬號要怎麼刪除Win10
- 不小心刪除,Mac電腦如何批次恢復檔案Mac
- php要小心的坑PHP
- 前端刪除多條資料,如何將多個被刪除項指定key傳給後臺前端
- [轉帖]Redis中刪除過期Key的三種策略Redis
- 要小心 JavaScript 的事件代理JavaScript事件
- Docker定時刪除none映象DockerNone
- 刪除vmvare的大檔案
- python 刪除大表資料Python
- oracle大資料量分批刪除Oracle大資料
- 【臨實戰】使用 Python 從 Redis 中刪除 4000W 個 KEYPythonRedis
- PSD檔案不小心刪除怎麼恢復?必備技能教學
- 不小心刪除IPone手機的資料?你也許需要這個
- 刪除臨時表空間組
- 大資料量刪除的思考(一)大資料
- 大資料量刪除的思考(三)大資料
- 大資料量刪除的思考(四)大資料
- 大資料量刪除的思考(二)大資料
- 大Key
- 同時使用 IB 和 Masonry 時,如何刪除 NSIBPrototypingLayoutConstraintAI
- Redis熱點key大keyRedis
- win10 無法刪除當前使用者要怎麼解決_win10系統刪除賬戶怎麼刪除不了Win10
- Laravel 中利用『模型事件』來實現刪除資料時的連帶刪除Laravel模型事件
- MySQL如何優雅的刪除大表MySql
- Oracle 刪除千萬級資料量時,可以考慮以下方法來提高刪除效率Oracle
- 刪除資料夾時顯示在另一程式開啟怎麼刪除
- [BUG反饋]刪除模型屬性時 不會刪除模型表的field_sort模型
- php(js)批量刪除/單個刪除PHPJS
- dbca刪除資料庫時選項灰色資料庫
- Linux刪除指定時間之前的檔案Linux