麻豆小视频在线观看_中文黄色一级片_久久久成人精品_成片免费观看视频大全_午夜精品久久久久久久99热浪潮_成人一区二区三区四区

首頁 > 數(shù)據(jù)庫 > Redis > 正文

redis超時(shí)問題分析

2020-03-17 12:41:08
字體:
供稿:網(wǎng)友

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

1. 網(wǎng)絡(luò)。Redis的處理與網(wǎng)絡(luò)息息相關(guān),如果網(wǎng)絡(luò)出現(xiàn)閃斷則容易發(fā)生redis超時(shí)的狀況。如果出現(xiàn)這種狀況首先應(yīng)查看redis機(jī)器網(wǎng)絡(luò)帶寬信息,判斷是否有閃斷情況發(fā)生。
2. 內(nèi)存。redis所有的數(shù)據(jù)都放在內(nèi)存里,當(dāng)物理內(nèi)存不夠時(shí),linux os會使用swap內(nèi)存,導(dǎo)致內(nèi)存交換發(fā)生,這時(shí)如果有redis調(diào)用命令就會產(chǎn)生redis超時(shí)。這里可以通過調(diào)整/proc/sys/vm/swappiness參數(shù),來設(shè)置物理內(nèi)存使用超過多少就會進(jìn)行swap。
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
另外還有一些特殊情況也會導(dǎo)致swap發(fā)生。當(dāng)我們使用rdb做為redis集群持久化時(shí)可能會發(fā)生物理內(nèi)存不夠的情況(aof持久化只是保持支持不斷的追加redis集群變化操作,不太容易引起swap)。當(dāng)使用rdb持久化時(shí),如程序1所示主進(jìn)程會fork一個(gè)子進(jìn)程去dump redis中所有的數(shù)據(jù),主進(jìn)程依然為客戶端服務(wù)。此時(shí)主進(jìn)程和子進(jìn)程共享同一塊內(nèi)存區(qū)域, linux內(nèi)核采用寫時(shí)復(fù)制來保證數(shù)據(jù)的安全性。在這種模式下如果客戶端發(fā)來寫請求,內(nèi)核將該頁賦值到一個(gè)新的頁面上并標(biāo)記為寫,在將寫請求寫入該頁面。因此,在rdb持久化時(shí),如果有其他請求,那么redis會使用更多的內(nèi)存,更容易發(fā)生swap,因此在可以快速恢復(fù)的場景下盡量少使用rdb持久化可以將rdb dump的條件設(shè)的苛刻一點(diǎn),當(dāng)然也可以選擇aof,但是aof也有他自身的缺點(diǎn)。另外也可以使用2.6以后的主從結(jié)構(gòu),將讀寫分離,這樣不會出現(xiàn)server進(jìn)程上又讀又寫的情景發(fā)生 3. Redis單進(jìn)程處理命令。Redis支持udp和tcp兩種連接,redis客戶端向redis服務(wù)器發(fā)送包含redis命令的信息,redis服務(wù)器收到信息后解析命令后執(zhí)行相應(yīng)的操作,redis處理命令是串行的具體流程如下。首先服務(wù)端建立連接如程序2所示,在創(chuàng)建socket,bind,listen后返回文件描述符:

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

程序2
對于redis這種服務(wù)來說,它需要處理成千上萬個(gè)連接(最高達(dá)到655350),需要使用多路復(fù)用來處理多個(gè)連接。這里redis提供了epoll,select, kqueue來實(shí)現(xiàn),這里在默認(rèn)使用epoll(ae.c)。拿到listen函數(shù)返回的文件描述符fd后,redis將fd和其處理acceptTcpHandler函數(shù)加入到事件驅(qū)動的鏈表中.實(shí)際上在加入事件隊(duì)列中,程序4事件驅(qū)動程序?qū)⑻捉幼窒嚓P(guān)的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->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
在初始話完所有事件驅(qū)動后,如程序5所示主進(jìn)程根據(jù)numevents = aeApiPoll(eventLoop, tvp)獲得io就緒的文件描述符和其對應(yīng)的處理程序,并對fd進(jìn)行處理。大致流程是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事件驅(qū)動結(jié)合epoll多路復(fù)用實(shí)現(xiàn)了串行式的命令處理。所以一些慢命令例如sort,hgetall,union,mget都會使得單命令處理時(shí)間較長,容易引起后續(xù)命令time out.所以我們第一需要從業(yè)務(wù)上盡量避免使用慢命令,如將hash格式改為kv自行解析,第二增加redis實(shí)例個(gè)數(shù),每個(gè)redis服務(wù)器調(diào)用盡量少的慢命令。


注:相關(guān)教程知識閱讀請移步到Redis頻道。
發(fā)表評論 共有條評論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 草久在线观看视频 | 中文欧美日韩 | 国产午夜精品久久久久婷 | 视频久久免费 | 午夜国产成人 | 国产一级做a爰片在线看 | 国产精品区一区二区三区 | 国人精品视频在线观看 | 国产中出在线观看 | 久久综合入口 | 久久久久久久久久久久99 | 成人在线激情视频 | 亚洲免费视频大全 | 国产激情视频在线 | 大学生一级毛片在线视频 | 免费一级a毛片免费观看 | 成人毛片在线 | 黄色av网| 91看片淫黄大片欧美看国产片 | 成人短视频在线播放 | 91在线精品亚洲一区二区 | 成人在线第一页 | 精品一区二区三区免费看 | 成年性羞羞视频免费观看无限 | 电影一级毛片 | 久色免费| 欧美一级片一区 | 羞羞视频免费网站男男 | 日本中文高清 | 女人解衣喂奶电影 | 国产精品一区二区三区在线看 | 啪啪毛片 | 色淫网站免费视频 | 日本视频在线免费观看 | 国产精品久久久久久久av | 国产免费成人在线 | 精品久久久久久久久久久下田 | 成年免费视频黄网站在线观看 | av免费在线观看免费 | 欧美一页 | 日本搞逼视频 |