文章目录

  • 前言
  • 1. redis是基于缓存的
  • 2. Redis为什么这么快?
  • 3.IO多路复用技术
  • 4.Redis线程模型
  • 4.1 I/O多路复用程序、文件事件分派器


前言

redis 是一个单线程却性能非常好的内存数据库, 主要用来作为缓存系统。 redis 采用网络IO多路复用技术来保证在多连接的时候, 系统的高吞吐量。


1. redis是基于缓存的

官方文档介绍说,Redis的操作都是基于内存的,CPU不是 Redis性能瓶颈,,Redis的瓶颈是机器内存和网络带宽

Reds是C语言写的,性能极高。单台redis情况下,官方提供的数据为:读的速度是110000次/s,写的速度是81000次/s 。

但是redis的线程模型表明,redis是单进程、单线程的。是不是很amazing!!!

2. Redis为什么这么快?

先说两个误区:一,高性能的服务器不一定都是多进程、多线程的;二、多线程不一定都不比单线程快,比如单核机器。

在我们通常的认知中,高性能都是通过多进程、多线程实现的。比如Nginx是多进程单线程的,Memcached是单进程多线程的。

在计算机的世界中,CPU的速度是远大于内存的速度的,同时内存的速度也是远大于硬盘的速度。redis的操作都是基于内存的,绝大部分请求是纯粹的内存操作,非常迅速,使用单线程可以省去多线程时CPU上下文会切换的时间,也不用去考虑各种锁的问题,不存在加锁释放锁操作,没有死锁问题导致的性能消耗。对于内存系统来说,多次读写都是在一个CPU上,没有上下文切换效率就是最高的!既然单线程容易实现,而且 CPU 不会成为瓶颈,那就顺理成章的采用单线程的方案了(毕竟采用多线程会有很多麻烦)。

那么Redis的单进程单线程模型的具体细节是怎样的?

3.IO多路复用技术

首先我们了解下IO多路复用技术
比如,现在我们模拟一个tcp服务器处理30个客户的socket,如何快速的处理掉这30个请求呢?

在不了解原理的情况下,我们类比一个实例:在课堂上让全班30个人同时做作业,做完后老师检查,30个学生的作业都检查完成才能下课。如何在有限的资源下,以最快的速度下课呢?

  • 第一种:安排一个老师,按顺序逐个检查。先检查A,然后是B,之后是C、D。。。这中间如果有一个学生卡住,全班都会被耽误。这种模式就好比,你用循环挨个处理socket,根本不具有并发能力。这种方式只需要一个老师,但是耗时时间会比较长。
  • 第二种:安排30个老师,每个老师检查一个学生的作业。
    这种类似于为每一个socket创建一个进程或者线程处理连接。这种方式需要30个老师(最消耗资源),但是速度最快。
  • 第三种:安排一个老师,站在讲台上,谁解答完谁举手。这时C、D举手,表示他们作业做完了,老师下去依次检查C、D的答案,然后继续回到讲台上等。此时E、A又举手,然后去处理E和A。这种方式可以在最小的资源消耗的情况下,最快的处理完任务。
    第三种就是IO复用模型(Linux下的select、poll和epoll就是干这个的。将用户socket对应的fd注册进epoll,然后epoll帮你监听哪些socket上有消息到达,这样就避免了大量的无用操作。此时的socket应该采用非阻塞模式。这样,整个过程只在调用select、poll、epoll这些调用的时候才会阻塞,收发客户消息是不会阻塞的,整个进程或者线程就被充分利用起来,这就是事件驱动,所谓的reactor模式。)

4.Redis线程模型

Redis基于Reactor模式开发了自己的网络事件处理器,被称为文件事件处理器,由套接字、I/O多路复用程序、文件事件分派器(dispatcher),事件处理器四部分组成。

redis io redis iops_客户端

4.1 I/O多路复用程序、文件事件分派器

  • I/O多路复用程序会同时监听多个套接字,当被监听的套接字准备好执行accept、read、write、close等操作时,与操作相对应的文件事件就会产生,I/O多路复用程序会将所有产生事件的套接字都压入一个队列,然后以有序地每次仅一个套接字的方式传送给文件事件分派器,文件事件分派器接收到套接字后会根据套接字产生的事件类型调用对应的事件处理器。

事件的处理器

redis io redis iops_redis io_02


(1)连接应答处理器:

当Redis服务器进行初始化的时候,程序会将这个连接应答处理器和服务器监听套接字的AEREADABLE事件关联起来,当有客户端用sys/socket.h/connect函数连接服务器监听套接字的时候,套接字就会产生AEREADABLE事件,引发连接应答处理器执行,并执行相应的套接字应答操作。

(2)命令请求处理器:

当一个客户端通过连接应答处理器成功连接到服务器之后,服务器会将客户端套接字的AEREADABLE事件和命令请求处理器关联起来,当客户端向服务器发送命令请求的时候,套接字就会产生AEREADABLE事件,引发命令请求处理器执行,并执行相应的套接字读入操作;

在客户端连接服务器的整个过程中,服务器都会一直为客户端套接字的AE_READABLE事件关联命令请求处理器。

(3)命令回复处理器:

当服务器有命令回复需要传送给客户端的时候,服务器会将客户端套接字的AEWRITABLE事件和命令回复处理器关联起来,当客户端准备好接收服务器传回的命令回复时,就会产生AEWRITABLE事件,引发命令回复处理器执行,并执行相应的套接字写入操作。

当命令发送完毕后,服务器会解除命令回复处理器与客户端套接字的AE_WRITABLE事件之间的关联。

注意1:只有当上一个套接字产生的事件被所关联的事件处理器执行完毕,I/O多路复用程序才会继续向文件事件分派器传送下一个套接字,所以对每个命令的执行时间是有要求的,如果某个命令执行过长,会造成其他命令的阻塞。所以慎用O(n)命令,Redis是面向快速执行场景的数据库。
注意2:命令的并发性。Redis是单线程处理命令,命令会被逐个被执行,假如有3个客户端命令同时执行,执行顺序是不确定的,但能确定不会有两条命令被同时执行,所以两条incr命令无论怎么执行最终结果都是2。

客户端与redis通信过程
1、假设一个Redis服务器正在运作,那么这个服务器的监听套接字的 AE_READABLE 事件应该正处于监听状态之下, 而该事件所对应的处理器为连接应答处理器。

2、如果这时有一个Redis客户端向服务器发起连接,那么监听套接字将产生 AEREADABLE事件,触发连接应答处理器执行。处理器会对客户端的连接请求进行应答,然后创建客户端套接字,以及客户端状态,并将客户端套接字的 AEREADABLE事件与命令请求处理器进行关联,使得客户端可以向主服务器发送命令请求。

3、之后,假设客户端向主服务器发送一个命令请求,那么客户端套接字将产生 AE_READABLE 事件,引发命令请求处理器执行,处理器读取客户端的命令内容,然后传给相关程序去执行。

4、执行命令将产生相应的命令回复, 为了将这些命令回复传送回客户端, 服务器会将客户端套接字的 AEWRITABLE 事件与命令回复处理器进行关联。当客户端尝试读取命令回复的时候, 客户端套接字将产生 AEWRITABLE 事件, 触发命令回复处理器执行, 当命令回复处理器将命令回复全部写入到套接字之后, 服务器就会解除客户端套接字的 AE_WRITABLE 事件与命令回复处理器之间的关联。