redis超时问题分析
redis超时问题分析
- 06/04. 2014
Redis在分布式应用中占据着越来越重要的地位,短短的几万行代码,实现了一个高性能的数据存储服务。最近dump中心的cm8集群出现过 几次redis超时的情况,但是查看redis机器的相关内存都没有发现内存不够,或者内存发生交换的情况,查看redis源码之后,发现在某些情况下 redis会出现超时的状况,相关细节如下。
int rdbSaveBackground(char *filename) {
pid_t childpid;
long long start; if (server.rdb_child_pid != -1) return REDIS_ERR;
server.dirty_before_bgsave = server.dirty;
server.lastbgsave_try = time(NULL);
start = ustime();
if ((childpid = fork()) == 0) {
int retval;
/* Child */
if (server.ipfd > 0) close(server.ipfd);
if (server.sofd > 0) close(server.sofd);
retval = rdbSave(filename);
if (retval == REDIS_OK) {
size_t private_dirty = zmalloc_get_private_dirty();
if (private_dirty) {
redisLog(REDIS_NOTICE,
"RDB: %zu MB of memory used by copy-on-write",
private_dirty/(1024*1024));
}
}
exitFromChild((retval == REDIS_OK) ? 0 : 1);
} else {
/* Parent */
server.stat_fork_time = ustime()-start;
if (childpid == -1) {
server.lastbgsave_status = REDIS_ERR;
redisLog(REDIS_WARNING,"Can't save in background: fork: %s",
strerror(errno));
return REDIS_ERR;
}
redisLog(REDIS_NOTICE,"Background saving started by pid %d",childpid);
server.rdb_save_time_start = time(NULL);
server.rdb_child_pid = childpid;
updateDictResizePolicy();
return REDIS_OK;
}
return REDIS_OK; /* unreached */
}
程序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后返回文件描述符:
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的监听事件中。
if (server.ipfd > 0 && aeCreateFileEvent(server.el,server.ipfd,AE_READABLE,
acceptTcpHandler,NULL) == AE_ERR) redisPanic("Unrecoverable error creating server.ipfd file event."); int aeCreateFileEvent(aeEventLoop *eventLoop, int fd, int mask,
aeFileProc *proc, void *clientData)
{
if (fd >= eventLoop->setsize) {
errno = ERANGE;
return AE_ERR;
}
aeFileEvent *fe = &eventLoop->events[fd]; if (aeApiAddEvent(eventLoop, fd, mask) == -1)
return AE_ERR;
fe->mask |= mask;
if (mask & AE_READABLE) fe->rfileProc = proc;
if (mask & AE_WRITABLE) fe->wfileProc = proc;
fe->clientData = clientData;
if (fd > eventLoop->maxfd)
eventLoop->maxfd = fd;
return AE_OK;
}
程序3
static int aeApiAddEvent(aeEventLoop *eventLoop, int fd, int mask) {
aeApiState *state = eventLoop->apidata;
struct epoll_event ee;
/* If the fd was already monitored for some event, we need a MOD
* operation. Otherwise we need an ADD operation. */
int op = eventLoop->events[fd].mask == AE_NONE ?
EPOLL_CTL_ADD : EPOLL_CTL_MOD;
ee.events = 0;
mask |= eventLoop->events[fd].mask; /* Merge old events */
if (mask & AE_READABLE) ee.events |= EPOLLIN;
if (mask & AE_WRITABLE) ee.events |= EPOLLOUT;
ee.data.u64 = 0; /* avoid valgrind warning */
ee.data.fd = fd;
if (epoll_ctl(state->epfd,op,fd,&ee) == -1) return -1;
return 0;
}
程序4
在初始话完所有事件驱动后,如程序5所示主进程根据numevents = aeApiPoll(eventLoop,
tvp)获得io就绪的文件描述符和其对应的处理程序,并对fd进行处理。大致流程是
accept()->createclient()->readQueryFromClient()。其中
readQueryFromClient()读取信息中的redis命令->
processInputBuffer()->call()最后完成命令。
void aeMain(aeEventLoop *eventLoop) {
eventLoop->stop = 0;
while (!eventLoop->stop) {
if (eventLoop->beforesleep != NULL)
eventLoop->beforesleep(eventLoop);
aeProcessEvents(eventLoop, AE_ALL_EVENTS);
}
}
int aeProcessEvents(aeEventLoop *eventLoop, int flags)
{-------------------------------
numevents = aeApiPoll(eventLoop, tvp);
for (j = 0; j < numevents; j++) { aeFileEvent *fe = &eventLoop->events[eventLoop->fired[j].fd];
int mask = eventLoop->fired[j].mask;
int fd = eventLoop->fired[j].fd;
int rfired = 0; /* note the fe->mask & mask & ... code: maybe an already processed
* event removed an element that fired and we still didn't
* processed, so we check if the event is still valid. */
if (fe->mask & mask & AE_READABLE) {
rfired = 1;
fe->rfileProc(eventLoop,fd,fe->clientData,mask);
}
if (fe->mask & mask & AE_WRITABLE) {
if (!rfired || fe->wfileProc != fe->rfileProc)
fe->wfileProc(eventLoop,fd,fe->clientData,mask);
}
processed++;
}
}
程序5
从上述代码可以看出redis利用ae事件驱动结合epoll多路复用实现了串行式的命令处理。所以一些慢命令例如
sort,hgetall,union,mget都会使得单命令处理时间较长,容易引起后续命令time
out.所以我们第一需要从业务上尽量避免使用慢命令,如将hash格式改为kv自行解析,第二增加redis实例个数,每个redis服务器调用尽量少
的慢命令。
redis超时问题分析的更多相关文章
- Redis源码分析:serverCron - redis源码笔记
[redis源码分析]http://blog.csdn.net/column/details/redis-source.html Redis源代码重要目录 dict.c:也是很重要的两个文件,主要 ...
- redis源码分析之事务Transaction(下)
接着上一篇,这篇文章分析一下redis事务操作中multi,exec,discard三个核心命令. 原文地址:http://www.jianshu.com/p/e22615586595 看本篇文章前需 ...
- Redis之阻塞分析
Redis是典型的单线程架构,所有的读写操作都是在一条主线程中完成的.当Redis用于高并发场景时,这条线程就变成了它的生命线.如果出现阻塞,哪怕是很短时间,对于我们的应用来说都是噩梦.导致阻塞问题的 ...
- Redis时延问题分析及应对
Redis时延问题分析及应对 Redis的事件循环在一个线程中处理,作为一个单线程程序,重要的是要保证事件处理的时延短,这样,事件循环中的后续任务才不会阻塞: 当redis的数据量达到一定级别后(比如 ...
- 您还有心跳吗?超时机制分析(java)
注:本人是原作者,首发于并发编程网(您还有心跳吗?超时机制分析),此文结合那里的留言作了一些修改. 问题描述 在C/S模式中,有时我们会长时间保持一个连接,以避免频繁地建立连接,但同时,一般会有一个超 ...
- ELK_elk+redis 搭建日志分析平台
这个是最新的elk+redis搭建日志分析平台,今年时间是2015年9月11日. Elk分别为 elasticsearch,logstash, kibana 官网为:https://www.elast ...
- linux下利用elk+redis 搭建日志分析平台教程
linux下利用elk+redis 搭建日志分析平台教程 http://www.alliedjeep.com/18084.htm elk 日志分析+redis数据库可以创建一个不错的日志分析平台了 ...
- Redis事务的分析及改进
Redis事务的分析及改进 Redis的事务特性 数据ACID特性满足了几条? 为了保持简单,redis事务保证了其中的一致性和隔离性: 不满足原子性和持久性: 原子性 redis事务在执行的中途遇到 ...
- 单点登录CAS使用记(七):关于服务器超时以及客户端超时的分析
我的预想情况 一般情况下,当用户登录一个站点后,如果长时间没有发生任何动作,当用户再次点击时,会被强制登出并且跳转到登录页面, 提醒用户重新登录.现在我已经为站点整合了CAS,并且已经实现了单点登录以 ...
随机推荐
- arcengine 实现调用arctoolbox中的dissolove
ESRI.ArcGIS.Geoprocessor.Geoprocessor geoprocessor = new Geoprocessor(); ESRI.ArcGIS.DataManagementT ...
- 网络爬虫的C++程序
[搜片神器]之DHT网络爬虫的C++程序初步开源 回应大家的要求,特地整理了一开始自己整合的代码,这样最简单,最直接的可以分析流程,至于文章里面提供的程序界面更多,需要大家自己开发. 谢谢园子朋友的支 ...
- (Sql Server)数据的拆分和合并
(Sql Server)数据的拆分和合并 背景: 今天遇到了数据合并和拆分的问题,尝试了几种写法.但大致可分为两类:一.原始写法.二.Sql Server 2005之后支持的写法.第一种写法复杂而且效 ...
- BOOST中read_some和 boost::asio::error::eof(2)错误
当socket读写完成调用回调函数时候一定要检查 是不是有EOF错误,如果有那么好了,另一方已经断开连接了别无选择,你也断开把. for (;;) { boost::array < char ...
- CENTOS下Python 升级后YUM无法使用的解决办法
Python有很多实用的工具,安装依赖python版本较高,升级Python后导致yum无法使用. 原因: 系统自带的yum依赖Python老版本,升级后不兼容 解决办法: 1. 列出所有版本,确定老 ...
- starling性能优化
在项目开发的过程中总结了一下starling的性能优化方案: 1. 不要频繁的读取显示对象的宽高:调用宽高属性时会调用显示对象的getBounds()方法,该方法会遍历该对象的所有子对象,重新 ...
- shell脚本兼容linux/unix与windows/cygwin的基础(注意处理好CR, LF, CR/LF 回车 换行的问题)
shell脚本兼容linux/unix与windows/cygwin的基础 :统一文本格式为:unix文本格式,即于LF为换行符(推荐方案) 在notepad上设置:编辑->档案格式转换-> ...
- vs2012中程序集生成无法自动在网站Bin目录下生成Dll文件?(已解决!)
最近,突然发现生成程序集后,网站bin目录下dll没有更新,也没有自动生成dll文件,通过近半个小时的摸索和实验,找到了解决方法: 1.右键网站,不是项目,选择[属性页],在左侧[引用]中如果没有,就 ...
- jquery代码实现简单的五星评价功能!
实现: 1,鼠标移动到第三个星星,则一二三星星变亮,后两个变暗 2,鼠标点击某个星星后,可以继续选择,但拿开后星星会定格住你点击的位置 <script type="text/javas ...
- Cstring 的用法
CString位于头文件afx.h中. 这篇文章就来讨论这些技巧. 使用CString可以让你对字符串的操作更加直截了当.这篇文章不是CString的完全手册,但囊括了大部分常见基本问题. 这篇文章包 ...