美文网首页
Redis单线程

Redis单线程

作者: 诺之林 | 来源:发表于2019-02-26 21:50 被阅读0次

目录

理解

Redis也会有多个线程

ps -ef | grep redis-server
# redis     5498     1  0 Jan29 ?        00:53:04 /usr/bin/redis-server 0.0.0.0:6379

ps -T 5498
#  PID  SPID TTY          TIME CMD
# 5498  5498 ?        00:53:04 redis-server
# 5498  5500 ?        00:00:00 redis-server
# 5498  5501 ?        00:00:00 redis-server
// redis.c
int main(int argc, char **argv) {
    initServer();
}

void initServer() {
    /* Create the serverCron() time event, that's our main way to process
     * background operations. */
    if (aeCreateTimeEvent(server.el, 1, serverCron, NULL, NULL) == AE_ERR) {
        redisPanic("Can't create the serverCron time event.");
        exit(1);
    }
}

int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
    /* Start a scheduled AOF rewrite if this was requested by the user while
     * a BGSAVE was in progress. */
    if (server.rdb_child_pid == -1 && server.aof_child_pid == -1 &&
        server.aof_rewrite_scheduled)
    {
        rewriteAppendOnlyFileBackground();
    }
}

// aof.c
int rewriteAppendOnlyFileBackground(void) {
    if ((childpid = fork()) == 0) {
        /* Child */
        char tmpfile[256];
        if (rewriteAppendOnlyFile(tmpfile) == REDIS_OK) {
            exitFromChild(0);
        } else {
            exitFromChild(1);
        }
    } else {
        /* Parent */
        return REDIS_OK;
    }
    return REDIS_OK;
}

int rewriteAppendOnlyFile(char *filename) {
    // 遍历所有数据库
    for (j = 0; j < server.dbnum; j++) {
        // 遍历数据库所有键,并通过命令将它们的当前状态(值)记录到新 AOF 文件中
        while((de = dictNext(di)) != NULL) {
        }
    }
}

Redis单线程处理事件

// redis.c
int main(int argc, char **argv) {
    initServer();
}

void initServer() {
    // 为 TCP 连接关联连接应答(accept)处理器
    // 用于接受并应答客户端的 connect() 调用
    for (j = 0; j < server.ipfd_count; j++) {
        if (aeCreateFileEvent(server.el, server.ipfd[j], AE_READABLE,
            acceptTcpHandler,NULL) == AE_ERR)
            {
                redisPanic(
                    "Unrecoverable error creating server.ipfd file event.");
            }
    }

    // 为本地套接字关联应答处理器
    if (server.sofd > 0 && aeCreateFileEvent(server.el,server.sofd,AE_READABLE,
        acceptUnixHandler,NULL) == AE_ERR) redisPanic("Unrecoverable error creating server.sofd file event.");
}

// ae.c
int aeCreateFileEvent(aeEventLoop *eventLoop, int fd, int mask,
        aeFileProc *proc, void *clientData)
{
    if (aeApiAddEvent(eventLoop, fd, mask) == -1)
        return AE_ERR;

    return AE_OK;
}

#ifdef HAVE_EVPORT
#include "ae_evport.c"
#else
    #ifdef HAVE_EPOLL
    #include "ae_epoll.c"
    #else
        #ifdef HAVE_KQUEUE
        #include "ae_kqueue.c"
        #else
        #include "ae_select.c"
        #endif
    #endif
#endif

// config.h
#ifdef __linux__
#define HAVE_EPOLL 1
#endif

// ae_epolll.c
static int aeApiAddEvent(aeEventLoop *eventLoop, int fd, int mask) {
    if (epoll_ctl(state->epfd,op,fd,&ee) == -1) return -1;

    return 0;
}
  • Redis高性能基于内存+多路复用I/O模型
1 支持一个进程打开大数目的fd(文件描述符)

2 轮询效率更高 => select/poll会轮询所有fd 而epoll只处理就绪的fd 它有一个就绪设备的队列 每次只轮询该队列的数据

3 内存效率更高 => 使用mmap加速内核与用户空间的消息传递
git clone https://gist.github.com/be7872baa82d9b9c400b17a2b0fe5fe3.git

gcc be7872baa82d9b9c400b17a2b0fe5fe3/reactor-demo.c -o reactor-demo

./reactor-demo
telnet localhost 8080
# Accpet a new client,client addr is 127.0.0.1 .

优势

  • 避免引入线程同步和锁机制(大大降低逻辑复杂度)

缺点

  • 无法发挥多核优势(可通过单机多Redis服务解决)

参考

相关文章

网友评论

      本文标题:Redis单线程

      本文链接:https://www.haomeiwen.com/subject/decnyqtx.html