redis 删除大key集合的方法
redis大key,这里指的是大的集合数据类型,如(set/hash/list/sorted set),一个key包含很多元素。由于redis是单线程,在删除大key(千万级别的set集合)的时候,或者清理过期大key数据时,主线程忙于删除这个大key,会导致redis阻塞、崩溃,应用程序异常的情况。
一个例子
线上redis作为实时去重的一个工具,里面有6千万的用户guid,这么一个set集合,如果直接使用del删除,会导致redis严重阻塞。
10.1.254.18:6380> info memory
# Memory
used_memory:15175740016
used_memory_human:14.13G
used_memory_rss:22302339072
used_memory_peak:22351749192
used_memory_peak_human:20.82G
used_memory_lua:36864
mem_fragmentation_ratio:1.47
mem_allocator:jemalloc-3.6.0
10.1.254.18:6380> scard helper_2019-03-12
(integer) 64530980
10.1.254.18:6380> del helper_2019-03-12
(integer) 1
(81.23s)
10.1.254.18:6380> info memory
# Memory
used_memory:8466985704
used_memory_human:7.89G
used_memory_rss:10669453312
used_memory_peak:22351749192
used_memory_peak_human:20.82G
used_memory_lua:36864
mem_fragmentation_ratio:1.26
mem_allocator:jemalloc-3.6.0
可以看到,helper_2019-03-12这个key,是一个包含64530980个元素的集合,直接使用del删除命令,花的时间为:81.23s,在超时时间短的苛刻情况下,显然会发送超时,程序异常!好在,我们用的是连接池,没有出现问题。
分批删除
这种情况,应该使用sscan命令,批量删除set集合元素的方法。下面是一个java的例子:
private static void test2(){
// 连接redis 服务器
Jedis jedis = new Jedis("0.0.0.0",6379);
jedis.auth("123456");
// 分批删除
try {
ScanParams scanParams = new ScanParams();
// 每次删除 500 条
scanParams.count(500);
String cursor = "";
while (!cursor.equals("0")){
ScanResult<String> scanResult=jedis.sscan("testset", cursor, scanParams);
// 返回0 说明遍历完成
cursor = scanResult.getStringCursor();
List<String> result = scanResult.getResult();
long t1 = System.currentTimeMillis();
for(int m = 0;m < result.size();m++){
String element = result.get(m);
jedis.srem("testset", element);
long t2 = System.currentTimeMillis();
System.out.println("删除"+result.size()+"条数据,耗时: "+(t2-t1)+"毫秒,cursor:"+cursor);
}catch (JedisException e){
e.printStackTrace();
}finally {
if(jedis != null){
jedis.close();
}
对于其它集合,也有对应的方法。
- hash key:通过hscan命令,每次获取500个字段,再用hdel命令;
- set key:使用sscan命令,每次扫描集合中500个元素,再用srem命令每次删除一个元素;
- list key:删除大的List键,未使用scan命令; 通过ltrim命令每次删除少量元素。
- sorted set key:删除大的有序集合键,和List类似,使用sortedset自带的zremrangebyrank命令,每次删除top 100个元素。
使用Python脚本批量删除
对于redis的监控和清理,通常会用一些Python脚本去做,简单、轻便。用java的话,再小的一个任务也要打包、发布,如果没有一套完善的开发、发布的流程,还是比较麻烦的。这时候,很多人倾向于写Python脚本,会Python的大部分人都是会Java的。
这里,还是以删除一个set集合为例:
# -*- coding:utf-8 -*-
import redis
def test():
# StrictRedis创建连接时,这个连接由连接池管理,所以我们无需关注连接是否需要主动释放
re = redis.StrictRedis(host = "0.0.0.0",port = 6379,password = "123")
key = "test"
for i in range(100000):
re.sadd(key, i)
cursor = '0'
cou = 200
while cursor != 0:
cursor,data = re.sscan(name = key, cursor = cursor, count = cou)
for item in data:
re.srem(key, item)
print cursor