当前位置: 首页 > 后端技术 > PHP

Redis命令DEL和UNLINK的区别,如何正确删除大key!

时间:2023-03-29 23:41:41 PHP

背??景在这篇文章中,我做了一个使用del命令删除大键的实验。结果是del命令随着key的增加而增加,主线程阻塞的时间越长。这与之前redis5.0.8版本代码中多线程删除操作的感觉不符,所以决定先查看redis关于删除操作的代码,找出重点,再进行实验验证。编写需要复用本文中使用的数据构造方法和测试脚本代码。代码分析步骤在server.c中找到redisCommandTable(命令表,redis的所有命令都在这个表中对应一个回调函数),找到del命令对应的回调函数delCommand。查看delCommand函数的代码内容如下:voiddelCommand(client*c){delGenericCommand(c,0);}delCommand继续简单调用一个通用的删除方法delGenericCommand,继续跟踪delGenericCommand,代码如下:/*这个命令实现了DEL和LAZYDEL。*/voiddelGenericCommand(client*c,intlazy){intnumdel=0,j;对于(j=1;jargc;j++){expireIfNeeded(c->db,c->argv[j]);int删除=懒惰?dbAsyncDelete(c->db,c->argv[j]):dbSyncDelete(c->db,c->argv[j]);如果(删除){signalModifiedKey(c->db,c->argv[j]);notifyKeyspaceEvent(NOTIFY_GENERIC,"del",c->argv[j],c->db->id);服务器脏++;数字++;}}addReplyLongLong(c,numdel);}这个方法比较有意思。首先注释说这个命令实现了删除和惰性删除(也就是我们想要的异步删除)。再看看这行代码:intdeleted=lazy?dbAsyncDelete(c->db,c->argv[j]):dbSyncDelete(c->db,c->argv[j]);也就是说根据参数lazy调用异步删除或同步删除的不同。那么这个方法在哪里调用,传递了哪些参数呢?根据ide的跟踪,发现调用当前函数的函数有2个,lazy参数传0,一个传1,刚好对应一个同步,一个异步。voiddelCommand(client*c){delGenericCommand(c,0);}voidunlinkCommand(client*c){delGenericCommand(c,1);}其中,delCommand是del命令的回调,unlinkCommand是del命令的回调取消链接命令。这意味着unlink命令将允许redis异步删除。构建数据的实验验证和php脚本的测试请参考本文。首先使用redis-cli--pipe向redis添加一个key为sigkey的hash数据,hlen为3000万,占用内存约1.8G。将key为m的string类型数据添加到redis中进行测试获取。使用redis-cli连接到redis。启动测试php脚本。在redis-cli中执行命令unlinkligkey。关闭观察php脚本,观察结果。带有标记信号的实验结果输出如下:0.570135001643333929--$4mack----------------xxx--------------------0.701077001643333929-$4MACK0.8081690016433333929-$4MACK0.924664001643333929-$4MACT0.03091900346357001643333930--$4mack0.458893001643333930--$4mack0.567871001643333930--$4mack--------------------xxx----------------------0.698115001643333930--$4mack可以看到两次波动的间隔0.13秒相差不大,而且这两次波动的时间节点与delete的执行不同命令(执行命令观察时没有标记),可以确认是正常的网络波动,排除unlinkblocking的影响。总结根据源码分析和实际实验操作,del命令使用同步删除,unlink使用异步删除。删除数据量小的简单类型时建议使用del命令,删除大key时使用unlink命令。之所以使用del删除小key,是因为del虽然是同步删除,会阻塞主线程,但是unlink也会对主线程进行一些判断等操作。而这些操作带来的开销可能比实际删除一个小key略多。因此,对于可以直接删除的键,不需要使用异步删除。作为补充,我们还可以通过配置让某些命令进行全局异步操作。###############################懒惰释放#####################################Redis有两个原语来删除键。一种称为DEL,是对象的阻塞#删除。这意味着服务器停止处理新命令#以便以同步#方式回收与对象关联的所有内存。如果删除的键与一个小对象相关联,则执行DEL命令所需的时间#非常小,可以与Redis中的大多数其他#O(1)或O(log_N)命令相媲美。然而,如果键与包含数百万元素的#聚合值相关联,服务器可能会阻塞#很长时间(甚至几秒钟)才能完成操作。##出于上述原因,Redis还提供了非阻塞删除原语#例如UNLINK(非阻塞DEL)和FLUSHALL和#FLUSHDB命令的ASYNC选项,以便在后台回收内存。这些命令#在恒定时间内执行。另一个线程将尽可能快地在后台逐步释放#对象。##DEL、UNLINK和ASYNCFLUSHALL和FLUSHDB的选项是用户控制的。#这取决于应用程序的设计来理解什么时候是一个好主意使用一个或另一个。然而,Redis服务器有时不得不#删除键或刷新整个数据库作为其他操作的副作用。#特别是Redis在#以下场景中独立于用户调用删除对象:##1)驱逐,因为最大内存和最大内存策略配置,#为了为新数据腾出空间,而不会超过指定的#内存限制。#2)由于过期:当具有相关生存时间的密钥(参见#EXPIRE命令)必须被删除来自内存。#3)由于将数据存储在可能#已经存在的键上的命令的副作用。例如,RENAME命令可能会删除旧的key#内容,当它被另一个替换时一个。同样,带有STORE选项的SUNIONSTORE#或SORT可能会删除现有键。SET命令#本身会删除指定键的任何旧内容,以便用指定的字符串替换#它。#4)在复制期间,当副本与其主服务器执行完全重新同步时,整个数据库的内容将被删除#加载刚刚传输的RDB文件。##在上述所有情况下,默认是以阻塞方式删除对象,#就像调用DEL一样。但是,您可以具体配置每种情况#,以便以非阻塞方式释放内存,就像调用UNLINK#一样,使用以下配置指令:lazyfree-lazy-evictionnolazyfree-lazy-expirenolazyfree-lazy-server-delnoreplica-lazy-flushnoredis的官方配置说明中明确表示del等命令是同步的,但是如果有需要,可以通过修改对应的配置值为yes来实现异步。