博客
关于我
redis 删除大key集合的方法
阅读量:410 次
发布时间:2019-03-06

本文共 4199 字,大约阅读时间需要 13 分钟。

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# Memoryused_memory:15175740016used_memory_human:14.13Gused_memory_rss:22302339072used_memory_peak:22351749192used_memory_peak_human:20.82Gused_memory_lua:36864mem_fragmentation_ratio:1.47mem_allocator:jemalloc-3.6.010.1.254.18:6380> scard helper_2019-03-12(integer) 6453098010.1.254.18:6380> del helper_2019-03-12(integer) 1(81.23s)10.1.254.18:6380> info memory# Memoryused_memory:8466985704used_memory_human:7.89Gused_memory_rss:10669453312used_memory_peak:22351749192used_memory_peak_human:20.82Gused_memory_lua:36864mem_fragmentation_ratio:1.26mem_allocator:jemalloc-3.6.0

可以看到,helper_2019-03-12这个key,是一个包含64530980个元素的集合,直接使用del删除命令,花的时间为:81.23s,显然会发送超时、阻塞,程序异常!好在,我们用的是连接池,没有出现问题。

Java 分批删除

这种情况,应该使用sscan命令,批量删除set集合元素的方法。下面是一个Java代码分批删除redis中set集合的例子:

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
scanResult=jedis.sscan("testset", cursor, scanParams); // 返回0 说明遍历完成 cursor = scanResult.getStringCursor(); List
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 redisdef 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 cursorif __name__ == '__main__':                    test()

后台删除之lazyfree机制

为了解决redis使用del命令删除大体积的key,或者使用flushdb、flushall删除数据库时,造成redis阻塞的情况,在redis 4.0引入了lazyfree机制,可将删除操作放在后台,让后台子线程(bio)执行,避免主线程阻塞。

lazy free的使用分为2类:第一类是与DEL命令对应的主动删除,第二类是过期key删除、maxmemory key驱逐淘汰删除。

主动删除

UNLINK命令是与DEL一样删除key功能的lazy free实现。唯一不同时,UNLINK在删除集合类键时,如果集合键的元素个数大于64个(详细后文),会把真正的内存释放操作,给单独的bio来操作。

127.0.0.1:7000> UNLINK mylist(integer) 1FLUSHALL/FLUSHDB ASYNC127.0.0.1:7000> flushall async //异步清理实例数据

被动删除

lazy free应用于被动删除中,目前有4种场景,每种场景对应一个配置参数; 默认都是关闭。

lazyfree-lazy-eviction nolazyfree-lazy-expire nolazyfree-lazy-server-del noslave-lazy-flush no

lazyfree-lazy-eviction

针对redis内存使用达到maxmeory,并设置有淘汰策略时;在被动淘汰键时,是否采用lazy free机制;

因为此场景开启lazy free, 可能使用淘汰键的内存释放不及时,导致redis内存超用,超过maxmemory的限制。此场景使用时,请结合业务测试。

lazyfree-lazy-expire

针对设置有TTL的键,达到过期后,被redis清理删除时是否采用lazy free机制;

此场景建议开启,因TTL本身是自适应调整的速度。

lazyfree-lazy-server-del

针对有些指令在处理已存在的键时,会带有一个隐式的DEL键的操作。如rename命令,当目标键已存在,redis会先删除目标键,如果这些目标键是一个big key,那就会引入阻塞删除的性能问题。 此参数设置就是解决这类问题,建议可开启。

slave-lazy-flush

针对slave进行全量数据同步,slave在加载master的RDB文件前,会运行flushall来清理自己的数据场景,

参数设置决定是否采用异常flush机制。如果内存变动不大,建议可开启。可减少全量同步耗时,从而减少主库因输出缓冲区爆涨引起的内存使用增长。

expire及evict优化

redis在空闲时会进入activeExpireCycle循环删除过期key,每次循环都会率先计算一个执行时间,在循环中并不会遍历整个数据库,而是随机挑选一部分key查看是否到期,所以有时时间不会被耗尽(采取异步删除时更会加快清理过期key),剩余的时间就可以交给freeMemoryIfNeeded来执行。

参考链接:

转载地址:http://gykuz.baihongyu.com/

你可能感兴趣的文章
ClickHouse源码笔记4:FilterBlockInputStream, 探寻where,having的实现
查看>>
Linux应用-线程操作
查看>>
多态体验,和探索爷爷类指针的多态性
查看>>
系统编程-进程间通信-无名管道
查看>>
记2020年初对SimpleGUI源码的阅读成果
查看>>
C语言实现面向对象方法学的GLib、GObject-初体验
查看>>
系统编程-进程-ps命令、进程调度、优先级翻转、进程状态
查看>>
为什么我觉得需要熟悉vim使用,难道仅仅是为了耍酷?
查看>>
一个支持高网络吞吐量、基于机器性能评分的TCP负载均衡器gobalan
查看>>
HDOJ2017_字符串统计
查看>>
高等软工第二次作业《需求分析阶段总结》
查看>>
404 Note Found 团队会议纪要
查看>>
CentOS安装Docker-ce并配置国内镜像
查看>>
使用JWT作为Spring Security OAuth2的token存储
查看>>
使用Redis作为Spring Security OAuth2的token存储
查看>>
【SOLVED】Linux使用sudo到出现输入密码提示延迟时间长
查看>>
springmvc转springboot过程中访问jsp报Whitelabel Error Page错误
查看>>
项目引入非配置的文件,打成war包后测试报错的可能原因
查看>>
Git学习笔记
查看>>
SpringBoot笔记
查看>>