基于redis的keys、scan刪除ttl為-1的key
前言:由于redis服務(wù)器最大使用內(nèi)存為450MB,redis存儲的部分key沒有設(shè)置過期時間,內(nèi)存淘汰策略為:noeviction 以上原因?qū)е聄edis服務(wù)無法加載到新的key,迫使部分業(yè)務(wù)受阻。
一、keys命令介紹
redis KEYS 命令基本語法如下:
redis 127.0.0.1:6379> KEYS PATTERN
返回值
符合給定模式的 key 列表 (Array)。
#獲取 redis 中所有的 key 可用使用 *。
redis 127.0.0.1:6379> KEYS *
1) "runoob3"
2) "runoob1"
3) "runoob2"
由于 Redis 是單線程在處理用戶的命令,而 Keys 命令會一次性遍歷所有 Key,于是在 命令執(zhí)行過程中,無法執(zhí)行其他命令。這就導(dǎo)致如果 Redis 中的 key 比較多,那么 Keys 命令執(zhí)行時間就會比較長,從而阻塞 Redis。
#查找以 runoob 為開頭的 key:
redis 127.0.0.1:6379> KEYS runoob*
1) "runoob3"
2) "runoob1"
3) "runoob2"
Keys 的缺點:
沒有l(wèi)imit,我們只能一次性獲取所有符合條件的key,如果結(jié)果有上百萬條,那么等待你的就是“無窮無盡”的字符串輸出。
keys命令是遍歷算法,時間復(fù)雜度是O(N)。如我們剛才所說,這個命令非常容易導(dǎo)致Redis服務(wù)卡頓。因此,我們要盡量避免在生產(chǎn)環(huán)境使用該命令。
二、scan命令介紹
redis Scan 命令基本語法如下:
SCAN cursor [MATCH pattern] [COUNT count]
參數(shù):
cursor - 游標。
pattern - 匹配的模式。
count - 指定從數(shù)據(jù)集里返回多少元素,默認值為 10 。
返回值:數(shù)組列表。
Scan命令就是對這個一維數(shù)組進行遍歷。每次返回的游標值也都是這個數(shù)組的索引。Count 參數(shù)表示遍歷多少個數(shù)組的元素,將這些元素下掛接的符合條件的結(jié)果都返回。因為每個元素下掛接的鏈表大小不同,所以每次返回的結(jié)果數(shù)量也就不同。
# 使用 0 作為游標,開始新的迭代
redis 127.0.0.1:6379> scan 0
1) "17" # 第一次迭代時返回的游標
2) 1) "key:12"
2) "key:8"
3) "key:4"
4) "key:14"
5) "key:16"
6) "key:17"
7) "key:15"
8) "key:10"
9) "key:3"
10) "key:7"
11) "key:1"
#使用的是第一次迭代時返回的游標 17 開始新的迭代
redis 127.0.0.1:6379> scan 17
1) "0"
2) 1) "key:5"
2) "key:18"
3) "key:0"
4) "key:2"
5) "key:19"
6) "key:13"
7) "key:6"
8) "key:9"
9) "key:11"Scan 的特點:
復(fù)雜度雖然也是 O(n),但是它是通過游標分步進行的,不會阻塞線程;
提供 limit 參數(shù),可以控制每次返回結(jié)果的最大條數(shù),limit 只是一個 hint,返回的結(jié)果可多可少;
同 keys 一樣,它也提供模式匹配功能;
服務(wù)器不需要為游標保存狀態(tài),游標的唯一狀態(tài)就是 scan 返回給客戶端的游標整數(shù);
返回的結(jié)果可能會有重復(fù),需要客戶端去重復(fù),這點非常重要;
遍歷的過程中如果有數(shù)據(jù)修改,改動后的數(shù)據(jù)能不能遍歷到是不確定的;
單次返回的結(jié)果是空的并不意味著遍歷結(jié)束,而要看返回的游標值是否為零
count參數(shù)詳解:
當count參數(shù)指定為100時,然而redis中key有幾百萬時,這個時候返回時間會很長,
count參數(shù)調(diào)大后,減少了交互次數(shù),返回的時間就會減少。
Count 參數(shù)越大,Redis 阻塞時間也會越長,需要取舍。
極限一點,Count 參數(shù)和總 Key 數(shù)一致時,Scan 命令就和 Keys 效果一樣了。
Count 大小和 Scan 總耗時的關(guān)系如下圖:

結(jié)論:
可以發(fā)現(xiàn) Count 越大,總耗時就越短,不過越后面提升就越不明顯了。
所以推薦的 Count 大小為 1W 左右。
如果不考慮 Redis 的阻塞,其實 Keys 比 Scan 會快很多,畢竟一次性處理,省去了多余的交互。
三、keys命令刪除
#!/usr/bin/python3
import redis
r = redis.Redis(host='172.18.158.92', port=6379, db=0,decode_responses=True)
var = 0
var1 = 0
list_keys = r.keys("system_WXMINI/WX_MINI_NO_Userinfo/unionid*")
for key in list_keys:
num = r.ttl(key)
if num == -1:
r.delete(key)
var = var + 1
else:
var1 = var1 + 1
print("end")
print("刪除key的數(shù)量",var)
print("未刪除的數(shù)量",var1)
四、scan命令刪除
[root@iZwz9conqz5shxfx2gmnfkZ scripts]# cat clean_key_v5.py
#!/usr/bin/python3
import redis
def RedisScan(vague_key,host="127.0.0.1",port=6379,password=None,db=0):
redis_cache = redis.Redis(host=host, port=port, db=db, password=password, decode_responses=True)
begin_pos,counts,var,delete_key = 0,0,0,0
while True:
begin_pos,list_keys = redis_cache.scan(begin_pos,vague_key,10000)
counts += len(list_keys)
for key in list_keys:
num = redis_cache.ttl(key)
if num == -1:
redis_cache.delete(key)
delete_key = delete_key + 1
else:
var = var + 1
if begin_pos == 0:
break
print("no delete key is ", var)
print("delete key is ", delete_key)
print("total key is ", counts)
#調(diào)用
RedisScan("system_url_id*","172.18.158.92",6379,"*****")
CONFIG SET maxmemory-policy volatile-lru
config set maxmemory 8589934592
