前因後果
之前我們的服務,在上線的時候發現有一些大Key的使用不是很規範,特别是沒有設定過期時間,是以導緻redis中記憶體的資料越來越多,目前Redis節點的記憶體已經快撐不住了。是以根據緩存鍵的規則去批量删除這些資料,比較常見的就是按字首去删除。
現在由于不得以為的原因要删除這幾百個Key-Value的資料,這個時候我們肯定就要把緩存鍵全部删除掉。一般情況下在Redis中是可以很容易去實作的。但是如果在不阻塞業務的前提下,并且以高效的方式進行清理記憶體資料。就需要好好想想辦法了。
批量删除redis資料方法
利用的是Linux的xargs指令
我們可以通過redis-cli的模式,進行通路之後登入到了Redis-Server服務,由于是必須要使用Linux的xargs指令,是以必須要連帶指令在Linux環境,而不能提前通過redis-cli進行登入到redis-server服務。否則會報錯說xargs無效。
redis-cli -h [ip] -p [port ] -a [password] keys "prefix*" | xargs redis-cli -h 127.0.0.1 -p 6379 -a '123' del
複制代碼
上面的指令主要由三部分連接配接組成:
- redis-cli -h [ip] -p [port ] -a [password]:主要需要用于登入到redis-cli的隻處理操作。
- keys "prefix*":随後主要是通過redis-cli的指令進行 keys指令進行比對某字首相關的資料集合。
- | xargs redis-cli -h [ip] -p [port ] -a [password] del:主要是通過管道符進行連接配接,之後再進行連接配接redis-server服務,之後進行将之前的參數傳入到xargs之後,作為del的參數進行執行删除操作。
xargs指令
xargs:是一條Unix和類Unix作業系統的常用指令;它的作用是将參數清單轉換成小塊分段傳遞給其他指令,以避免參數清單過長的問題。可單獨使用,也可使用管道符、重定位符等與其他指令配合使用。
xargs [ -p ] [ -t] [ -e[ EOFString ] ] [ -EEOFString ] [ -i[ ReplaceString ] ] [ -IReplaceString ] [ -l [ Number ] ] [ -L Number ] [ -n Number [ -x ] ] [ -s Size ] [ Command [ Argument ... ] ]
複制代碼
指令格式
xargs:一般是和管道一起使用。
somecommand |xargs -item command
複制代碼
參數:
- -a file 從檔案中讀入作為 stdin
- -e flag ,注意有的時候可能會是-E,flag必須是一個以空格分隔的标志,當xargs分析到含有flag這個标志的時候就停止。
- -p 當每次執行一個argument的時候詢問一次使用者。
- -n num 後面加次數,表示指令在執行的時候一次用的argument的個數,預設是用所有的。
- -t 表示先列印指令,然後再執行。
- -i 或者是-I,這得看linux支援了,将xargs的每項名稱,一般是一行一行指派給 {},可以用 {} 代替。
- -r no-run-if-empty 當xargs的輸入為空的時候則停止xargs,不用再去執行了。
- -s num 指令行的最大字元數,指的是 xargs 後面那個指令的最大指令行字元數。
- -L num 從标準輸入一次讀取 num 行送給 command 指令。
- -l 同 -L。
- -d delim 分隔符,預設的xargs分隔符是回車,argument的分隔符是空格,這裡修改的是xargs的分隔符。
- -x exit的意思,主要是配合-s使用。。
- -P 修改最大的程序數,預設是1,為0時候為as many as it can ,這個例子我沒有想到,應該平時都用不到的吧。
使用Lua腳本删除百萬/千萬級的key
如果以上xargs方法删除不了的,或者執行xargs指令報錯的。那麼可以使用lua腳本,redis有内置的lua解釋器。在lua腳本中使用scan掃描key,并依次删除,當删除數量達到1萬時,腳本直接傳回,完成本次調用,如果删除的key數量大于0,就循環調用腳本進行删除。
Warning: a NUL character occurred in the input. It cannot be passed through in the argument list. Did you mean to use the --null option?
複制代碼
Lua腳本是什麼?
Lua是一種輕量小巧的腳本語言,用标準C語言編寫并以源代碼形式開放, 其設計目的是為了嵌入應用程式中,進而為應用程式提供靈活的擴充和定制功能。其設計目的是為了嵌入應用程式中,進而為應用程式提供靈活的擴充和定制功能。
Lua腳本的指令格式
有興趣的小夥伴,可以參考:redis.cn/commands/ev…
EVAL script numkeys key [key …] arg [arg …]
複制代碼
- script:待執行的腳本檔案
- numkeys:key的個數
Lua腳本執行參數
- [key …]:對應的key,可以是一個,可以是多個
- [arg …]:與key對應的值,可以是一個,可以是多個
Lua擷取傳參資料
Lua的下表索引是從1開始的,key的擷取方式,KEYS[下标索引],如KEYS[1],取第一個值,值的擷取,ARGV[1]
示例
eval “return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}” 2 key1 key2 first second
複制代碼
Lua腳本的案例(keys)
- 擷取傳入的需要批量删除的key的字首
- 記住 lua的下标索引是從1開始 不是0 不是0 不是0
local key = KEYS[1]
if( key ~= nil) then
--這裡通過keys查詢出所有符合條件的資料
local dataList = redis.call('keys',KEYS[1])
--判斷是否找到資料
if(dataList ~= nil) then
--循環删除
for i=1,#dataList,1 do
redis.call('del',dataList[i])
end
--傳回删除的行數
return #dataList
else
return 0
end
else
return 0
end
複制代碼
推薦使用scan擷取資料删除,我們知道redis是一個單線程的,當我們庫裡面存在大量資料的時候,使用keys * 的方式比對資料的時候,可能需要好幾秒才能處理完,那麼在這個幾秒的時間裡是處于線程阻塞的,其他所有的redis操作都是處于等待狀态,這樣對系統的可用性是有影響的,是以,這裡使用scan的方式比對資料。
scan介紹
SCAN cursor [MATCH pattern] [COUNT count]
複制代碼
SCAN 指令是一個基于遊标的疊代器(cursor based iterator): SCAN 指令每次被調用之後, 都會向使用者傳回一個新的遊标, 使用者在下次疊代時需要使用這個新遊标作為 SCAN 命 令的遊标參數, 以此來延續之前的疊代過程。
Lua腳本的案例(scan)
local limitSize = tonumber(ARGV[1]) -- 最多删除多少個key
local batchSize = limitSize -- scan一次最多掃描多少個key
if (batchSize > 10000) then -- 一次掃描不能超過1w條
batchSize = 10000
end
local function scan(key)
local cursor = 0
local keynum = 0
repeat
local res = redis.call("scan", cursor, "match", key, 'COUNT', batchSize)
if (res ~= nil and #res >= 0) then
redis.replicate_commands()
cursor = tonumber(res[1])
local ks = res[2]
local size = #ks
for i=1,size,1 do
redis.call("del", tostring(ks[i]))
keynum = keynum + 1
if (keynum >= limitSize) then -- 已經删除了指定數量的key, 傳回
return keynum
end
end
end
until (cursor <= 0)
return keynum
end
local total = scan(KEYS[1])
return total
複制代碼
當 SCAN 指令的遊标參數被設定為 0 時, 伺服器将開始一次新的疊代, 而當伺服器向使用者傳回值為 0 的遊标時, 表示疊代已結束。
通俗點了解就是,基于遊标的疊代器redis會慢慢一次次的将資料傳回回來,進而防止線程阻塞。
此外還有一個小貼士就是可以使用UNLINK删除,差別于del的是這個是異步執行的,這條指令要版本大于4.0.0 小于4.0.0就使用del
redis.call("UNLINK",key)
複制代碼
原文連結:https://juejin.cn/post/7190954379535450167