怎樣分析Redis超時

這期內(nèi)容當中小編將會給大家?guī)碛嘘P怎樣分析redis超時,文章內(nèi)容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。

成都網(wǎng)站設計、網(wǎng)站建設服務團隊是一支充滿著熱情的團隊,執(zhí)著、敏銳、追求更好,是創(chuàng)新互聯(lián)的標準與要求,同時竭誠為客戶提供服務是我們的理念。創(chuàng)新互聯(lián)公司把每個網(wǎng)站當做一個產(chǎn)品來開發(fā),精雕細琢,追求一名工匠心中的細致,我們更用心!

Redis在分布式應用中占據(jù)著越來越重要的地位,短短的幾萬行代碼,實現(xiàn)了一個高性能的數(shù)據(jù)存儲服務。最近dump中心的cm8集群出現(xiàn)過幾次redis超時的情況,但是查看redis機器的相關內(nèi)存都沒有發(fā)現(xiàn)內(nèi)存不夠,或者內(nèi)存發(fā)生交換的情況,查看redis源碼之后,發(fā)現(xiàn)在某些情況下redis會出現(xiàn)超時的狀況,相關細節(jié)如下。

1. 網(wǎng)絡。Redis的處理與網(wǎng)絡息息相關,如果網(wǎng)絡出現(xiàn)閃斷則容易發(fā)生redis超時的狀況。如果出現(xiàn)這種狀況首先應查看redis機器網(wǎng)絡帶寬信息,判斷是否有閃斷情況發(fā)生。

2. 內(nèi)存。redis所有的數(shù)據(jù)都放在內(nèi)存里,當物理內(nèi)存不夠時,linux os會使用swap內(nèi)存,導致內(nèi)存交換發(fā)生,這時如果有redis調(diào)用命令就會產(chǎn)生redis超時。這里可以通過調(diào)整/proc/sys/vm/swappiness參數(shù),來設置物理內(nèi)存使用超過多少就會進行swap。

int rdbSaveBackground(char *filename) {     pid_t childpid;     long long start;       if (server.rdb_child_pid != -1) return REDIS_ERR;     serverserver.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發(fā)生。當我們使用rdb做為redis集群持久化時可能會發(fā)生物理內(nèi)存不夠的情況(aof持久化只是保持支持不斷的追加redis集群變化操作,不太容易引起swap)。當使用rdb持久化時,如程序1所示主進程會fork一個子進程去dump redis中所有的數(shù)據(jù),主進程依然為客戶端服務。此時主進程和子進程共享同一塊內(nèi)存區(qū)域, linux內(nèi)核采用寫時復制來保證數(shù)據(jù)的安全性。在這種模式下如果客戶端發(fā)來寫請求,內(nèi)核將該頁賦值到一個新的頁面上并標記為寫,在將寫請求寫入該頁面。因此,在rdb持久化時,如果有其他請求,那么redis會使用更多的內(nèi)存,更容易發(fā)生swap,因此在可以快速恢復的場景下盡量少使用rdb持久化可以將rdb dump的條件設的苛刻一點,當然也可以選擇aof,但是aof也有他自身的缺點。另外也可以使用2.6以后的主從結構,將讀寫分離,這樣不會出現(xiàn)server進程上又讀又寫的情景發(fā)生 3. Redis單進程處理命令。Redis支持udp和tcp兩種連接,redis客戶端向redis服務器發(fā)送包含redis命令的信息,redis服務器收到信息后解析命令后執(zhí)行相應的操作,redis處理命令是串行的具體流程如下。首先服務端建立連接如程序2所示,在創(chuàng)建socket,bind,listen后返回文件描述符:

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

程序2

對于redis這種服務來說,它需要處理成千上萬個連接(***達到655350),需要使用多路復用來處理多個連接。這里redis提供了epoll,select, kqueue來實現(xiàn),這里在默認使用epoll(ae.c)。拿到listen函數(shù)返回的文件描述符fd后,redis將fd和其處理acceptTcpHandler函數(shù)加入到事件驅動的鏈表中.實際上在加入事件隊列中,程序4事件驅動程序將套接字相關的fd文件描述符加入到epoll的監(jiān)聽事件中。

 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->clientDataclientData = 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所示主進程根據(jù)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多路復用實現(xiàn)了串行式的命令處理。所以一些慢命令例如sort,hgetall,union,mget都會使得單命令處理時間較長,容易引起后續(xù)命令time out.所以我們需要從業(yè)務上盡量避免使用慢命令,如將hash格式改為kv自行解析,第二增加redis實例個數(shù),每個redis服務器調(diào)用盡量少的慢命令。

上述就是小編為大家分享的怎樣分析Redis超時了,如果剛好有類似的疑惑,不妨參照上述分析進行理解。如果想知道更多相關知識,歡迎關注創(chuàng)新互聯(lián)行業(yè)資訊頻道。

當前標題:怎樣分析Redis超時
分享URL:http://muchs.cn/article36/piecpg.html

成都網(wǎng)站建設公司_創(chuàng)新互聯(lián),為您提供全網(wǎng)營銷推廣虛擬主機、App開發(fā)網(wǎng)頁設計公司、靜態(tài)網(wǎng)站、營銷型網(wǎng)站建設

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉載內(nèi)容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉載,或轉載時需注明來源: 創(chuàng)新互聯(lián)

商城網(wǎng)站建設