源自:https://xie.infoq.cn/article/0e34856ec9a88d749e7b1ae7a
前提
redis中存在很多key,可能随着業務的下架永遠也用不到了,需要批量删除(當然也可以不處理,等redis記憶體不足的時候,自動去執行淘汰政策)。假設存在若幹個如下模式的key,都是以 unkey 為字首,現在希望批量删除。

截屏2020-07-16 下午2.43.17.png
redis 2.6.0 之前
通過 del
直接删除(不推薦)
del
redis官方中的
del
是不支援正規表達式的,但是你可以從網上找到相關的辦法讓
del
指令支援正則,因為不推薦,所有不展示
通過 shell
實作
shell
redis中的 KEYS 是支援通過正規表達式擷取比對的key,然後通過管道傳遞給 xargs 進行删除
redis-cli KEYS "unkey*" | xargs redis-cli DEL
image.png
redis 2.6.0 之後
通過Lua腳本删除
redis 2.6.0 之後的版本支援通過 EVAL 或 EVALSHA 執行使用Lua。 可以保證操作的原子性,還可以通過
script load
對腳本進行緩存,減少網絡開銷。使用lua 有更高的自由度,可以添加很多功能子產品。
return redis.call('del',unpack(redis.call('keys','unkey*')))
基本流程是這樣的,如果邏輯嚴謹的話,可以動态傳入比對的表達式,傳回删除的key之類的邏輯。
-- 參數1: “un*” 正規表達式
local keys=redis.call('keys',ARGV[1]);
if #keys==0 then
return '--No match--'
else
redis.call('del',unpack(keys))
return keys
end
截屏2020-07-16 下午2.38.59.png
redis 2.8.0 之後
通過 SCAN 替代 KEYS
KEYS
redis的執行是單線程的(至少目前版本是),如果我們通過
KEYS
去批量的擷取key,如果數量很大的話,會有問題:
- 沒有limit,隻能一次擷取所有,如果資料量很大的話全部輸出的話,互動不友好。
- keys 是周遊算法,時間複雜度是 O(n),如果資料量特别大的話,可能會導緻服務卡頓,影響到正常業務。
如果我們用
SACN
替代的話,就會好很多:
- 時間複雜度雖然也是 O(n),但是它是通過遊标分步進行的,不會阻塞線程
- 提供limit參數,控制傳回的條數
-- 參數1: “un*” 正規表達式
-- 每次擷取1000條(不一定傳回1000條),進行删除
local cursor="0";
repeat
local t = redis.call('scan',cursor,'match',ARGV[1],'count',1000);
local list=t[2];
if #list==0 then
return '--over--'
end
redis.call('del',unpack(list));
cursor=t[1];
until cursor=="0"
return "over";
但是這樣寫是有問題的,這還是一次擷取所有key,雖然每次都擷取1000條,但是lua代碼原子執行,還是會有一次擷取很多阻塞線程的問題。
所有建議控制 每次擷取的數量,然後多次執行,每次傳回的遊标給下次執行使用。
-- 參數1: “0” 遊标
-- 參數2: “un*” 正規表達式
local t = redis.call('scan',ARGV[1],'match',ARGV[2],'count',1000);
local list=t[2];
if #list==0 then
return '--over--'
end
redis.call('del',unpack(list));
return t[1];
redis 4.0.0 之後
通過 UNLINK 替代 DEL
DEL
正常的
del
是在主線程内執行,等待目前的
del
執行完成,下一個指令才會執行。而redis在4.0之後提供了更好的選擇。
unlink
會在另一個線程中執行記憶體的回收,不會阻塞正常的get set請求,而且使用的方式也很簡單,跟執行
del
一樣。
是以說redis 4.0 之後就不再是單線程了,但是基本的操作還是在主線程内執行。
-- 參數1: “0” 遊标
-- 參數2: “un*” 正規表達式
local t = redis.call('scan',ARGV[1],'match',ARGV[2],'count',1000);
local list=t[2];
if #list==0 then
return '--over--'
end
redis.call('unlink',unpack(list));
return t[1];