大批量删除Redis中的key

本文遵循BY-SA版权协议,转载请附上原文出处链接。


本文作者: 黑伴白

本文链接: http://heibanbai.com.cn/posts/b1e35cf8/

由于早期使用redis时,没有指定key过期时间,导致大量内存浪费,需要修改合理指定过期时间,并对历史数据进行清理

方案1:使用Linux的xargs命令以及keys+del批量删除

Redis 中有删除单个 Key 的指令 DEL,但好像没有批量删除 Key 的指令,不过我们可以借助 Linux 的 xargs 指令来完成这个动作

1
2
3
redis-cli keys "base*" | xargs redis-cli del  
//如果redis-cli没有设置成系统变量,需要指定redis-cli的完整路径
//如:/opt/redis/redis-cli keys "base*" | xargs /opt/redis/redis-cli del

如果要指定 Redis 数据库访问密码,使用下面的命令

1
redis-cli -a password keys "base*" | xargs redis-cli -a password del

如果要访问 Redis 中特定的数据库,使用下面的命令

1
2
//下面的命令指定数据序号为0,即默认数据库  
redis-cli -n 0 keys "base*" | xargs redis-cli -n 0 del

删除所有Key,可以使用Redis的flushdb和flushall命令

1
2
3
4
//删除当前数据库中的所有Key  
flushdb
//删除所有数据库中的key
flushall

此方案有两个问题:

  • keys命令是阻塞的,会影响redis的线上流量正常使用

  • keys命中的key量多了之后,执行keys命令时间会很长,同时xargs拼接del命令的时候会超出参数的长度限制

方案2:使用Linux的xargs命令以及scan+del批量删除

参考redis官方手册,使用scan命令查找key不会阻塞

1
2
3
4
5
begin

redis-cli -h localhost -p 6379 -a password scan 0 match "base*" count 1000 | xargs redis-cli -h localhost -p 6379 -a password del

end

此方案的问题:

  • count小的话要执行次数太多,需要脚本化循环处理才有可能实施
  • count大的话xargs拼接del命令的时候也会超出参数的长度限制

方案3:使用shell脚本及scan+del批量删除

参考网友现成的方案,考虑key多执行时间长以及日志较多,不挂起执行脚本实现批量删除,并删除不挂起执行日志文件防止日志文件过大

参数1:需要删除key的匹配字符串

参数2:redis的host

参数3:redis的端口

参数4:redis的db

参数5:每次删除的个数

参数6:每次批量删除的间隔秒数,支持小数

脚本执行的linux必须安装有redis客户端

脚本内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#!/bin/bash

if [ "$#" -lt 3 ]
then
echo "Scan keys in Redis matching a pattern using SCAN (safe version of KEYS)"
echo "Usage: $0 [pattern] <host> [port] [database] [count] [second]"
exit 1
fi
pattern=${1:-}
host=${2:-}
port=${3:-6379}
database=${4:-0}
count=${5:-5000}
second=${6:-1}

if [ ! -n "$pattern" ] ;then
echo "pattern shoud not be empty!"
fi

cursor=-1

keys=''

while [ $cursor -ne 0 ]; do

if [ $cursor -eq -1 ]
then
cursor=0
fi

reply=`redis-cli -h "$host" -p "$port" -n "$database" SCAN $cursor MATCH $pattern COUNT $count`

cursor=`expr "$reply" : '\([0-9]*[0-9]\)'`

keys=${reply#[0-9]*[[:space:]]}

redis-cli -h "$host" -p "$port" -n "$database" DEL $keys

sleep $second

done

执行脚本:

1
2
3
4
5
6
7
begin

sudo nohup sh scantodel.sh *pattern* 127.0.0.1 6379 0 5000 1

rm -f nohup.out

end

方案4:使用shell脚本及scan+del批量删除-方案3脚本优化

相当于是对方案3脚本的另一种实现方式,也算是对方案3中脚本的优化

当count比较大,这两个计算时可能出现错误:

cursor=expr "$reply" : '\([0-9]*[0-9]\)'

keys=${reply#[0-9]*[[:space:]]}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#!/bin/bash
#定义变量《根据个人需要填写》
redis_url=${1:-}
redis_port=${2:-6379}
redis_db=${3:-0}
scan_patten=${4:-}
scan_count=${5:-1000}
redis_pass=${6:-}

if [ "X${redis_pass}" == "X" ];then
redis_info="redis-cli -h $redis_url -p $redis_port -n $redis_db"
else
redis_info="redis-cli -h $redis_url -p $redis_port -a $redis_pass -n $redis_db"
fi

yb=0
#0:循环获取游标进行遍历
counter=0
while 1>0
do
counter=$((counter+1))
#1:扫描获取结果
array=(`$redis_info scan $yb match $scan_patten count $scan_count`)
#2:设置游标值
yb=${array[0]}
#3:获取key值
arrLen=${#array[@]}
keysNum=`expr $arrLen - 1`
if [ $keysNum -gt 0 ]; then
start=`date +"%Y%m%d %H:%M:%S"`
#4: 删除key
$redis_info del ${array[@]:1:$keysNum}
end=`date +"%Y%m%d %H:%M:%S"`
echo "counter: [$counter] yb: [$yb] keysNum: [$keysNum]"
echo "del time: [$start] - [$end]"
else
echo "counter: [$counter] yb: [$yb] keysNum: [$keysNum]"
echo "no keys to del"
fi
#5:判断条件结束循环
if [ $yb -eq 0 ]; then
break
fi
unset array
done

蚂蚁再小也是肉🥩!


大批量删除Redis中的key
http://heibanbai.com.cn/posts/b1e35cf8/
作者
黑伴白
发布于
2023年9月7日
许可协议

“您的支持,我的动力!觉得不错的话,给点打赏吧 ୧(๑•̀⌄•́๑)૭”

微信二维码

微信支付

支付宝二维码

支付宝支付