Redis超时问题分析汇总

Redis在分布式应用中占据着越来越重要的地位,短短的几万行代码,实现了一个高性能的数据存储服务。最近dump中心的cm8集群出现过几次redis超时的情况,但是查看redis机器的相关内存都没有发现内存不够,或者内存发生交换的情况,查看redis源码之后,发现在某些情况下redis会出现超时的状况,相关细节如下。 2014-03-18 13:27:55 Redis数据存储 Nginx Httpcode分析脚本 本文中通过每5分钟对Nginx日志进行切割,然后用Python程序计算http code的分布,用Zabbix来实现单台机器Nginx qos的监控,配合对Zabbix数据库的Lastvalue进行聚合,可以详细的各个维度的分析。具体Nginx日志使用的分析脚本如正文所示。 2014-03-18 10:27:20 Nginx Httpc脚本 Hadoop部署常用的小脚本 最近抛弃非ssh连接的Hadoop集群部署方式了,还是回到了用ssh key 验证的方式上了。这里面就有些麻烦,每台机器都要上传公钥。恰恰我又是个很懒的人,所以写几个小脚本完成,只要在一台机器上面就可以做公钥的分发了。 2014-03-18 10:19:55 Hadoop部署hadoop集群脚本 2014运维趋势技术:自动化和SDN 原创 本文管中窥豹,大胆预测2014年IT运维方向的趋势技术,分别是:运维自动化技术和SDN/NFV(软件定义网络/网络虚拟化),希望能够为各位同行起到抛砖引玉的作用,将运维管理人员将会从重复、简单、枯燥的工作中解脱出来。 2014-03-18 09:43:17 运维趋势技术自动化运维 Openshift 安装使用教程 OpenShift是红帽公司推出的一个云计算服务平台,配置自己的openshift可以通过几种方式,比如web端,命令行。本文是介绍命令行下的配置,毕竟修改代码什么的都需要在本地修改后再提交。 2014-03-18 09:27:30 Openshift 如何确定远程计算机所用的操作系统? 本文分享的这个小技巧会告诉你如何使用nmap命令来确定远程计算机所运行的操作系统。如果你要创建你的局域网主机的清单列表,或者你根本不知道某些本地或远程IP地址背后运行的是什么系统,你需要一些提示,此时这个技巧可能会帮上大忙。 2014-03-17 15:38:07 远程计算机操作系统 动手实战SQL Server死锁 最近的一个项目由于客户明确提出要做下性能压力测试,使用的工具就是VS自带的压力测试工具。以前其它项目做压力测试后反馈的其中一个重要问题就是数据库的死锁。没想到我们这个项目测试时死锁同样的发生了,我之前的项目由于很少参与压力测试,基本上也不会去了解死锁,以及死锁如何解决的问题。 2014-03-17 10:34:48 SQL Server Linux cp 命令示例:创建文件和目录的副本 拷贝文件和目录是每一个操作系统的基本指令。备份行为基本上是创建文件和目录的副本。在Linux系统下,我们可以用cp命令来实现。本文介绍了15个Linux cp命令实例,对此感兴趣的朋友们多多关注。 2014-03-17 10:27:08 Linux cp备份 Linux运维工程师应如何挑选数据库服务器? Linux运维工程师的工作不仅要和操作Linux操作系统还要接触数据库,几乎每个企业都会用到数据库,如何选择数据库服务器成了工程师们必须面对的一大难题。本文中给大家提供了几条选择数据库服务器的小建议。一起来看看。 2014-03-14 13:40:29 Linux运维工程师数据库服务器 Zabbix监控报警统计信息报表功能 用zabbix来监控报警,由于出现时间久了、报警次数多了、人员忙了等情况,这些会导致一些报警内容被忽略,所以需要一个报表来反应谁收到了什么样的报警信息,收到了多少条,从而来判断这些情况是否被处理及处理进度,以及可以更好的分配人力资源。 2014-03-13 15:14:19 Zabbix监控报警Zabbix Zabbix企业应用 :固定端口监控Redis 本文介绍使用固定端口模式监控redis,先展示效果图,看看是否能满足你的需求,然后再看看具体的监控配置方案。 2014-03-13 14:56:34 Zabbix端口监控 五款好玩又好用的Linux网络测试和监控工具 译文 本文介绍了几款Linux网络测试实用工具,分别是:Bandwidthd、Speedometer、Nethogs、Darkstat和iperf,跟踪带宽使用情况和网络速度、查找网络资源消耗大户,以及测试性能。

Redis在分布式应用中占据着越来越重要的地位,短短的几万行代码,实现了一个高性能的数据存储服务。最近dump中心的cm8集群出现过几次redis超时的情况,但是查看redis机器的相关内存都没有发现内存不够,或者内存发生交换的情况,查看redis源码之后,发现在某些情况下redis会出现超时的状况,相关细节如下。

Redis在分布式应用中占据着越来越重要的地位,短短的几万行代码,实现了一个高性能的数据存储服务。最近dump中心的cm8集群出现过几次redis超时的情况,但是查看redis机器的相关内存都没有发现内存不够,或者内存发生交换的情况,查看redis源码之后,发现在某些情况下redis会出现超时的状况,相关细节如下。

1. 网络。Redis的处理与网络息息相关,如果网络出现闪断则容易发生redis超时的状况。如果出现这种状况首先应查看redis机器网络带宽信息,判断是否有闪断情况发生。

2. 内存。redis所有的数据都放在内存里,当物理内存不够时,linux os会使用swap内存,导致内存交换发生,这时如果有redis调用命令就会产生redis超时。这里可以通过调整/proc/sys/vm/swappiness参数,来设置物理内存使用超过多少就会进行swap。

  1. intrdbSaveBackground(char*filename){
  2. pid_tchildpid;
  3. longlongstart;
  4. if(server.rdb_child_pid!=-1)returnREDIS_ERR;
  5. serverserver.dirty_before_bgsave=server.dirty;
  6. server.lastbgsave_try=time(NULL);
  7. start=ustime();
  8. if((childpid=fork())==0){
  9. intretval;
  10. /*Child*/
  11. if(server.ipfd>0)close(server.ipfd);
  12. if(server.sofd>0)close(server.sofd);
  13. retval=rdbSave(filename);
  14. if(retval==REDIS_OK){
  15. size_tprivate_dirty=zmalloc_get_private_dirty();
  16. if(private_dirty){
  17. redisLog(REDIS_NOTICE,
  18. "RDB:%zuMBofmemoryusedbycopy-on-write",
  19. private_dirty/(1024*1024));
  20. }
  21. }
  22. exitFromChild((retval==REDIS_OK)?0:1);
  23. }else{
  24. /*Parent*/
  25. server.stat_fork_time=ustime()-start;
  26. if(childpid==-1){
  27. server.lastbgsave_status=REDIS_ERR;
  28. redisLog(REDIS_WARNING,"Can'tsaveinbackground:fork:%s",
  29. strerror(errno));
  30. returnREDIS_ERR;
  31. }
  32. redisLog(REDIS_NOTICE,"Backgroundsavingstartedbypid%d",childpid);
  33. server.rdb_save_time_start=time(NULL);
  34. server.rdb_child_pid=childpid;
  35. updateDictResizePolicy();
  36. returnREDIS_OK;
  37. }
  38. returnREDIS_OK;/*unreached*/
  39. }

程序1

另外还有一些特殊情况也会导致swap发生。当我们使用rdb做为redis集群持久化时可能会发生物理内存不够的情况(aof持久化只是保持支持不断的追加redis集群变化操作,不太容易引起swap)。当使用rdb持久化时,如程序1所示主进程会fork一个子进程去dump redis中所有的数据,主进程依然为客户端服务。此时主进程和子进程共享同一块内存区域, linux内核采用写时复制来保证数据的安全性。在这种模式下如果客户端发来写请求,内核将该页赋值到一个新的页面上并标记为写,在将写请求写入该页面。因此,在rdb持久化时,如果有其他请求,那么redis会使用更多的内存,更容易发生swap,因此在可以快速恢复的场景下尽量少使用rdb持久化可以将rdb dump的条件设的苛刻一点,当然也可以选择aof,但是aof也有他自身的缺点。另外也可以使用2.6以后的主从结构,将读写分离,这样不会出现server进程上又读又写的情景发生 3. Redis单进程处理命令。Redis支持udp和tcp两种连接,redis客户端向redis服务器发送包含redis命令的信息,redis服务器收到信息后解析命令后执行相应的操作,redis处理命令是串行的具体流程如下。首先服务端建立连接如程序2所示,在创建socket,bind,listen后返回文件描述符:

  1. server.ipfd=anetTcpServer(server.neterr,server.port,server.bindaddr);

程序2

对于redis这种服务来说,它需要处理成千上万个连接(***达到655350),需要使用多路复用来处理多个连接。这里redis提供了epoll,select, kqueue来实现,这里在默认使用epoll(ae.c)。拿到listen函数返回的文件描述符fd后,redis将fd和其处理acceptTcpHandler函数加入到事件驱动的链表中.实际上在加入事件队列中,程序4事件驱动程序将套接字相关的fd文件描述符加入到epoll的监听事件中。

  1. if(server.ipfd>0&&aeCreateFileEvent(server.el,server.ipfd,AE_READABLE,
  2. acceptTcpHandler,NULL)==AE_ERR)redisPanic("Unrecoverableerrorcreatingserver.ipfdfileevent.");
  3. intaeCreateFileEvent(aeEventLoop*eventLoop,intfd,intmask,
  4. aeFileProc*proc,void*clientData)
  5. {
  6. if(fd>=eventLoop->setsize){
  7. errno=ERANGE;
  8. returnAE_ERR;
  9. }
  10. aeFileEvent*fe=&eventLoop->events[fd];
  11. if(aeApiAddEvent(eventLoop,fd,mask)==-1)
  12. returnAE_ERR;
  13. fe->mask|=mask;
  14. if(mask&AE_READABLE)fe->rfileProc=proc;
  15. if(mask&AE_WRITABLE)fe->wfileProc=proc;
  16. fe->clientDataclientData=clientData;
  17. if(fd>eventLoop->maxfd)
  18. eventLoop->maxfd=fd;
  19. returnAE_OK;
  20. }

程序3

  1. staticintaeApiAddEvent(aeEventLoop*eventLoop,intfd,intmask){
  2. aeApiState*state=eventLoop->apidata;
  3. structepoll_eventee;
  4. /*Ifthefdwasalreadymonitoredforsomeevent,weneedaMOD
  5. *operation.OtherwiseweneedanADDoperation.*/
  6. intop=eventLoop->events[fd].mask==AE_NONE?
  7. EPOLL_CTL_ADD:EPOLL_CTL_MOD;
  8. ee.events=0;
  9. mask|=eventLoop->events[fd].mask;/*Mergeoldevents*/
  10. if(mask&AE_READABLE)ee.events|=EPOLLIN;
  11. if(mask&AE_WRITABLE)ee.events|=EPOLLOUT;
  12. ee.data.u64=0;/*avoidvalgrindwarning*/
  13. ee.data.fd=fd;
  14. if(epoll_ctl(state->epfd,op,fd,&ee)==-1)return-1;
  15. return0;
  16. }

程序4

在初始话完所有事件驱动后,如程序5所示主进程根据numevents = aeApiPoll(eventLoop, tvp)获得io就绪的文件描述符和其对应的处理程序,并对fd进行处理。大致流程是accept()->createclient()->readQueryFromClient()。其中readQueryFromClient()读取信息中的redis命令-> processInputBuffer()->call()***完成命令。

  1. voidaeMain(aeEventLoop*eventLoop){
  2. eventLoop->stop=0;
  3. while(!eventLoop->stop){
  4. if(eventLoop->beforesleep!=NULL)
  5. eventLoop->beforesleep(eventLoop);
  6. aeProcessEvents(eventLoop,AE_ALL_EVENTS);
  7. }
  8. }
  9. intaeProcessEvents(aeEventLoop*eventLoop,intflags)
  10. {-------------------------------
  11. numevents=aeApiPoll(eventLoop,tvp);
  12. for(j=0;j<numevents;j++){aeFileEvent*fe=&eventLoop->events[eventLoop->fired[j].fd];
  13. intmask=eventLoop->fired[j].mask;
  14. intfd=eventLoop->fired[j].fd;
  15. intrfired=0;
  16. /*notethefe->mask&mask&...code:maybeanalreadyprocessed
  17. *eventremovedanelementthatfiredandwestilldidn't
  18. *processed,sowecheckiftheeventisstillvalid.*/
  19. if(fe->mask&mask&AE_READABLE){
  20. rfired=1;
  21. fe->rfileProc(eventLoop,fd,fe->clientData,mask);
  22. }
  23. if(fe->mask&mask&AE_WRITABLE){
  24. if(!rfired||fe->wfileProc!=fe->rfileProc)
  25. fe->wfileProc(eventLoop,fd,fe->clientData,mask);
  26. }
  27. processed++;
  28. }
  29. }

程序5

从上述代码可以看出redis利用ae事件驱动结合epoll多路复用实现了串行式的命令处理。所以一些慢命令例如sort,hgetall,union,mget都会使得单命令处理时间较长,容易引起后续命令time out.所以我们***需要从业务上尽量避免使用慢命令,如将hash格式改为kv自行解析,第二增加redis实例个数,每个redis服务器调用尽量少的慢命令。

©本文为清一色官方代发,观点仅代表作者本人,与清一色无关。清一色对文中陈述、观点判断保持中立,不对所包含内容的准确性、可靠性或完整性提供任何明示或暗示的保证。本文不作为投资理财建议,请读者仅作参考,并请自行承担全部责任。文中部分文字/图片/视频/音频等来源于网络,如侵犯到著作权人的权利,请与我们联系(微信/QQ:1074760229)。转载请注明出处:清一色财经

(0)
打赏 微信扫码打赏 微信扫码打赏 支付宝扫码打赏 支付宝扫码打赏
清一色的头像清一色管理团队
上一篇 2023年5月6日 17:34
下一篇 2023年5月6日 17:34

相关推荐

发表评论

登录后才能评论

联系我们

在线咨询:1643011589-QQbutton

手机:13798586780

QQ/微信:1074760229

QQ群:551893940

工作时间:工作日9:00-18:00,节假日休息

关注微信