天天看點

我想模糊删除 redis key

源自:https://xie.infoq.cn/article/0e34856ec9a88d749e7b1ae7a

前提

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

我想模糊删除 redis key

截屏2020-07-16 下午2.43.17.png



redis 2.6.0 之前

通過 ​

del

 直接删除(不推薦)

redis官方中的 ​

del

 是不支援正規表達式的,但是你可以從網上找到相關的辦法讓 ​

del

 指令支援正則,因為不推薦,所有不展示

通過 ​

shell

 實作

redis中的 ​KEYS  是支援通過正規表達式擷取比對的key,然後通過管道傳遞給 ​xargs 進行删除

redis-cli KEYS "unkey*" | xargs redis-cli DEL



我想模糊删除 redis key

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



我想模糊删除 redis key

截屏2020-07-16 下午2.38.59.png



redis 2.8.0 之後

通過 ​SCAN 替代 ​

KEYS

redis的執行是單線程的(至少目前版本是),如果我們通過 ​

KEYS

 去批量的擷取key,如果數量很大的話,會有問題:

  1. 沒有limit,隻能一次擷取所有,如果資料量很大的話全部輸出的話,互動不友好。
  2. keys 是周遊算法,時間複雜度是 O(n),如果資料量特别大的話,可能會導緻服務卡頓,影響到正常業務。

如果我們用 ​

SACN

 替代的話,就會好很多:

  1. 時間複雜度雖然也是 O(n),但是它是通過遊标分步進行的,不會阻塞線程
  2. 提供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

 執行完成,下一個指令才會執行。而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];



繼續閱讀