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

Redis大key删除真的会导致主线程阻塞吗?做实验来证明吧!

时间:2023-03-29 15:26:52 PHP

背??景网上很多关于redis的话题都讲避免创建大key,因为删除会导致主线程阻塞。看到评论说测试删除一个2G的大key后系统卡死80秒左右。我在采访中被问到如何删除一个大密钥。最近在看redis5.0.8的源码,看到大key的删除其实只是在主线程删除key相关的数据,而实际的value及其内存释放是在异步删除线程上进行的。这种操作应该没有网上说的主线程阻塞80秒那么可怕。准备一份redis5.0.x服务器程序用于测试(为了方便,这里使用docker搭建)。php7.3(不用其他,系统自带7.3版本),其他脚本语言也可以。c编译器,添加数据的程序是用c开发的(因为内置的php没有安装pcntl扩展=,=,所以利用c的多线程并发构造和添加数据),这里主要用于构造数据,您可以使用其他语言。构造数据,编写数据构造代码,基于macos平台编写,可根据实际操作系统稍作调整。文笔比较粗糙。添加不同数量的数据,修改服务器端信息,需要修改宏定义,然后重新编译。#include#include#include#include#include#include#include#include#include#include#include#include#include#include#defineRESV_BUFF_SIZE512#defineSEND_BUFF_SIZE1024/***redis服务器IP地址*/#defineDEST_ADDR"172.20.23.83"/***redis服务器端口号*/#defineDEST_PORT6379/***每个线程执行的insert动作总数*/#defineTRANS_PER_THREAD10000/***每隔insert次数打印一次日志*/#defineLOG_STEP255/***打开的线程总数*/#defineTHREADS_NUM100intnumLen(intnum){inti=0;做{num/=10;我++;}while(num!=0);returni;}/***线程执行代码*@parampfrom0线程号-99用于判断当前线程要插入的数据段*例如数字为1则插入数据从TRANS_PER_THREAD*1到TRANS_PER_THREAD*1+TRANS_PER_THREAD*/voidthreadMain(void*p){intsockfd,*index=p,rlen;*index*=TRANS_PER_THREAD;intend=*index+TRANS_PER_THREAD;结构sockaddr_indest_addr;bzero(&(dest_addr),sizeof(dest_addr));charresvbuf[RESV_BUFF_SIZE];charsendbuf[SEND_BUFF_SIZE];sockfd=socket(AF_INET,SOCK_STREAM,0);if(sockfd==-1){printf("套接字创建失败:%d!\n",sockfd);}dest_addr.sin_family=AF_INET;dest_addr.sin_port=htons(DEST_PORT);inet_pton(AF_INET,DEST_ADDR,&dest_addr.sin_addr);if(connect(sockfd,(structsockaddr*)&dest_addr,sizeof(structsockaddr))==-1){printf("连接失败:%d!\n",errno);错误(“错误:”);}else{printf("连接成功!\n");结构时间电视,ltv;for(inti=*index;i*index){printf("已使用%ld.%d秒。\n",tv.tv_sec-ltv.tv_sec,tv.tv_usec-ltv.tv_usec);fflush(标准输出);}ltv=电视;}}}printf("完成索引:%d.\n",*index);close(sockfd);}voidhandle_pipe(intsig){//printf("sig%dignore.\n",sig);}intmain(){/***由于tcp客户端关闭,服务器可能仍然向我们发送数据*这会导致我们的进程接收到SIGPIPE信号*所以在这里注册信号处理函数*/structsigactionsa;sa.sa_handler=handle_pipe;sigemptyset(&sa.sa_mask);sa.sa_flags=0;sigaction(SIGPIPE,&sa,NULL);pthread_t点[THREADS_NUM];intindexes[THREADS_NUM];结构时间电视,ltv;gettimeofday(<v,NULL);/***创建线程*/for(inti=0;i0.13){echo"----------------xxx----------------------\n";}echomicrotime().'--'.$s;睡眠(100000);$st=$et;}socket_close($socket);执行测试使用redis-cli连接redis服务器。设置一个字符串类型数据,key为m。执行php脚本进行观察。redis-cli执行删除命令。关闭php脚本。查找------------------xxx--------------------标记。观察标记前后两个输出之间的时间间隔长度。测试结果migkey:0.270183001643271634--$4mack----------------xxx------------------0.665999001643271634--$4mackbigkey(虽然后面两个标记的时间间隔超过0.13秒,但是相差不远,可以判断是网络波动造成的,这里要观察第一个标记前后的时间差。):0.238428001643271538--$4mack----------------xxx--------------------0.765452001643271538--$4mack----------------xxx--------------------0.900372001643271538--$4mack0.009098001643271539--$4mack----------------xxx---------------------0.481983001643271539--$4macksigkey:未观察到波动。初步结果得出结论,5.5M密钥不会造成明显阻塞。大约58M个密钥会导致大约50毫秒的阻塞。继续构建更大的数据和网上说的2G数据差太远,需要构建更大的数据。由于数据是通过网络客户端构建的,即使是多线程,构建效率仍然不高,所以这次大数据的构建采用了特殊的方式(使用带pipe参数的redis-cli)。使用php脚本构建一个包含3000万条redis命令的文件0.txt。脚本如下: