背景

由于早期使用redis时,没有指定key过期时间,导致大量内存浪费,排查修改合理指定过期时间,历史数据的清理比我想象中有难度

各种方案尝试

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

参考阿里云开发者社区文章,使用Linux的xargs命令以及keys+del批量删除

begin

sudo redis-cli -h localhost -p 6379 -a password keys "tryCurrentWorkerSwitch*" | xargs sudo redis-cli -h localhost -p 6379 -a password del

end

此方案有两个问题:

  1. keys命令是阻塞的,会影响redis的线上流量正常使用
  2. keys命中的key量多了之后,执行keys命令时间会很长,同时xargs拼接del命令的时候会超出参数的长度限制

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

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

begin

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

end

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

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

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

scantodel.sh内容:

#!/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

执行脚本

begin

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

rm -f nohup.out

end

实操效果

思考

除了前面提到的没有设置过期时间,还有个问题是这种量级的key。千万级别的key听起来很牛的样子,实际憨得不行,完全可以冗余hash对key进行归类,当然也需要避免一个hash元素过大,需要合理的设计key及其结构。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐