likes
comments
collection
share

Redis 网络模型

作者站长头像
站长
· 阅读数 74

Redis 网络模型

I/O 基本模型

Redis 网络模型

读操作:内核将数据写入 内核缓冲区,通过系统调用把 内核缓冲区中的数据拷贝的用户缓冲区中

写操作:用户把数据写入 用户缓冲区,通过系统调用把 用户缓冲区中的数据写入到内核缓冲区中

阻塞 I/O

Redis 网络模型

阶段一:

  • 用户进程尝试获取数据;
  • 数据未到达,内核处于等待状态
  • 用户进程处于阻塞状态

阶段二

  • 内核接收到数据,将数据拷贝到用户缓冲区中
  • 该阶段用户进程也处于阻塞状态

非阻塞 I/O

Redis 网络模型

阶段一:

  • 不断执行系统调用,判断内核是否准备好数据;
  • 此时用户进程处于轮询判断的状态,用户进程不阻塞

阶段二:

  • 内核接收到数据,将数据拷贝到用户缓冲区中
  • 该阶段用户进程也处于阻塞状态

I/O 多路复用

利用单个线程监听多个 socket,并在某个 socket 可读、可写时得到通知

Redis 网络模型

阶段一:

  • 用户进程调用 select,监听 socket 集合
  • 任意一个或多个 socket 就绪就会返回 readable;
  • 用户进程处于阻塞状态

阶段二:

  • 用户进程找到可读的 socket
  • 依次调用 recvfrom 读取数据
  • 内核将数据拷贝到用户空间
  • 用户进程处理数据

select 和 poll 相当于是当被监听的数据准备好之后,他会把你监听的 FD 整个数据都发给你,你需要到整个FD中去找,哪些是处理好了的,需要通过遍历的方式,所以性能也并不是那么好,而 epoll,则相当于内核准备好了之后,他会把准备好的数据,直接发给你,咱们就省去了遍历的动作

select

用户态将监听的 fds 数组(大小为 1024)传入内核态,内核遍历 fds 数组,如果某个数据准备好就将该数组 fds 写会到用户态中,用户态遍历整个数组,找到就绪的 fd

poll

  • 创建 pollfd 数组,向其中添加关注的 fd 信息,数组大小自定义
  • 调用 poll 函数,将 pollfd 数组拷贝到内核空间,转链表存储,无上限
  • 内核遍历fd,判断是否就绪
  • 数据就绪或超时后,拷贝 pollfd 数组到用户空间,返回就绪 fd 数量 n
  • 用户进程判断n是否大于 0,大于 0 则遍历 pollfd 数组,找到就绪的 fd

与select对比:

  • select模式中的 fd_set 大小固定为 1024,而 pollfd 在内核中采用链表,理论上无上限
  • 监听 FD 越多,每次遍历消耗时间也越久,性能反而会下降

epoll

epoll_create:创建 一个 eventepoll 结构体,包含红黑树,用来记录监听的 fd,链表,记录就绪的 fd

epoll_ctl:把监听的 fd 注册到红黑树上,并为每一个 fd 设置一个监听函数 callback,这个函数会在 fd 就绪时触发,现在准备好了,就会把 fd 注册到 list_head 中

epoll_wait:等待 fd 事件就绪,在用户空间创建一个 event 数组,将链表中就绪的 fd 拷贝到 event 数组中

select 模式存在的三个问题:

  • 能监听的 FD 最大不超过 1024
  • 每次 select 都需要把所有要监听的 FD 都拷贝到内核空间
  • 每次都要遍历所有 FD 来判断就绪状态

poll 模式的问题:

  • poll利用链表解决了 select 中监听 FD 上限的问题,但依然要遍历所有 FD,如果监听较多,性能会下降

epoll 模式中如何解决这些问题的?

  • 基于epoll实例中的红黑树保存要监听的FD,理论上无上限,而且增删改查效率都非常高
  • 每个FD只需要执行一次 epoll_ctl 添加到红黑树,以后每次 epol_wait 无需传递任何参数,无需重复拷贝FD到内核空间
  • 利用ep_poll_callback机制来监听FD状态,无需遍历所有FD,因此性能不会随监听的FD数量增多而下降

Redis 网络模型

网络模型

Redis 网络模型

Redis 的网络模型是基于 I/O 多路复用 + 事件委派的机制

  1. I/O 多路复用

是指利用单个线程来同时监听多个 Socket,并在某个 Socket 可读、可写时得到通知,从而避免无效的等待,充分利用 CPU 资源。目前的 I/O 多路复用都是采用的 epoll 模式实现,它会在通知用户进程 Socket 就绪的同时,把已就绪的 Socket 写入用户空间,不需要挨个遍历 Socket 来判断是否就绪,提升了性能。

  1. Redis 网络模型

就是使用 l/O 多路复用结合事件的处理器来应对多个 Socket 请求

  • 连接应答处理器
  • 命令回复处理器,在 Redis6.0 之后,为了提升更好的性能,使用了多线程来处理回复事件
  • 命令请求处理器,在 Redis6.0 之后,将命令的转换使用了多线程,增加命令转换速度,在命令执行的时候,依然是单线程
转载自:https://juejin.cn/post/7243756043523227705
评论
请登录