其他

Redis高性能模型

2017-06-01 程序源

一、引言

Redis是基于单进程单线程模型的,即对于客户端的所有读写等请求的处理,都由一个主线程串行地处理。因此当多个客户端同时对一个键进行写操作不会有并发问题。

我们知道Redis Server不仅处理客户端读写等请求,还有一些自身的比较复杂且耗时的操作,如持久化RDB和AOF文件等。Redis为了高效的处理客户端的事件,并没有将持久化文件放在主线程里面进行处理,而是Redis在适当的时机fork子进程来异步的处理这种任务。

Redis会fork子进程进行处理持久化文件操作,Redis还有一组异步任务处理线程,用于处理不需要主线程同步处理的工作,即处理一些低级别的事件。

二、Redis单进程单线程

上面我们提到Redis中异步任务处理线程,其被封装在BIO组件中。下面我们来看下bio.h,bio.c

/* Exported API */ void bioInit(void); void bioCreateBackgroundJob(int type, void *arg1, void *arg2, void *arg3); unsigned long long bioPendingJobsOfType(int type); void bioWaitPendingJobsLE(int type, unsigned long long num); time_t bioOlderJobOfType(int type); void bioKillThreads(void); /* Background job opcodes */ #define BIO_CLOSE_FILE    0 /* Deferred close(2) syscall. */ #define BIO_AOF_FSYNC     1 /* Deferred AOF fsync. */ #define BIO_NUM_OPS       2bioProcessBackgroundJobs() /* Process the job accordingly to its type. */ if (type == BIO_CLOSE_FILE) {    close((long)job->arg1); } else if (type == BIO_AOF_FSYNC) {    aof_fsync((long)job->arg1); } else {    serverPanic("Wrong job type in bioProcessBackgroundJobs()."); } /* Kill the running bio threads in an unclean way. This function should be * used only when it's critical to stop the threads for some reason. * Currently Redis does this only on crash (for instance on SIGSEGV) in order * to perform a fast memory check without other threads messing with memory. */ void bioKillThreads(void) {    int err, j;    for (j = 0; j < BIO_NUM_OPS; j++) {        if (pthread_cancel(bio_threads[j]) == 0) {            if ((err = pthread_join(bio_threads[j],NULL)) != 0) {                serverLog(LL_WARNING,                    "Bio thread for job type #%d can be joined: %s",                        j, strerror(err));            } else {                serverLog(LL_WARNING,                    "Bio thread for job type #%d terminated",j);            }        }    } }

从上述的代码中,可以看到BIO有三种类型的线程,分别处理三种类型的事件:

  • 文件句柄关闭

  • AOF持久化

  • 空间懒释放

三、Redis的高性能

在实际应用,我们经常会采用高性能软件Memcached、Nginx。然而Memcached、Nginx的高性能采用了不同的处理模型,Memcached是单进程多线程模型,Nginx是多进程单线程模型。

Redis的高性能又是如何做到的哪?

我们可以从如下几个方面分析:

  1. 绝大部分操作是基于纯粹的内存

  2. 相对简单的数据结构,Redis全程使用hash结构,读取速度快;还有一些特殊的数据结构,对数据存储进行了优化,如压缩表,对数据进行压缩存储;再如跳表,使用有序的数据结构加快读取的速度

  3. 单线程避免不必要的上线文切换及竞争条件

  4. I/O多路复用模型

       旨在解决IO的问题。多路I/O复用模型是非阻塞IO,内部实现采用epoll和自己实现的事件分离框架。

  • 其利用select、poll、epoll 可以同时检测多个流的 I/O 事件的能力,在空闲的时候,会把当前线程阻塞掉,当有一个或多个流有 I/O 事件时,就从阻塞态中唤醒,于是程序就会轮询一遍所有的流(epoll 是只轮询那些真正发出了事件的流),并且只依次顺序的处理就绪的流,这种做法就避免了大量的无用操作。

  • “多路”指的是多个网络连接,“复用”指的是复用同一个线程。采用多路 I/O 复用技术可以让单个线程高效的处理多个连接请求(尽量减少网络 IO 的时间消耗),且 Redis 在内存中操作数据的速度非常快(内存内的操作不会成为这里的性能瓶颈)。

以上几点使Redis具有很高的吞吐量。

Redis工作流程

四、小结

Redis单进程单线程模型高性能得益于Redis的异步化组件,其处理策略是将主要的工作由主线程处理,非主要工作交于子进程和异步线程组处理;在一定程度上降低了主线程的压力,进而提高了吞吐量。

非核心逻辑异步化,提高主进程的处理效率。

五、参考

http://blog.jobbole.com/103012/


最近热文:

邀你参加11期 程序员专场相亲活动

加入群请加微信2518988391(备注岗位)

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存