Redis_高性能IO模型

redis核心结束与实战 - 03

通常说Redis 是单线程,主要是指 Redis 的网络 IO 和键值对读写是由一个线程来完成的,这也是 Redis 对外提供键值存储服务的主要流程。但 Redis 的其他功能,比如持久化、异步删除、集群数据同步等,其实是由额外的线程执行的。

由于多线程存在很多额外的开销,所以Redis采用的单线程处理网络I/O与键值对的读写。又因为基本I/O存在潜在阻塞点,因此利用Socket支持的非阻塞模式实现了基于多路复用的高性能I/O,从而实现了单线程Redis的高性能

目录

一、多线程带来的开销:

二、单线程 Redis 为什么那么快?

【依赖底层高性能的数据结构,依赖于多路复用机制】

1、基本I/O模型及其阻塞点   (潜在阻塞点:客户端与Redis建立连接, Redis读取客户端数据)

2、非阻塞模式

3、基于多路复用的高性能I/O模型


一、多线程带来的开销:

多线程编程模式面临的共享资源的并发访问控制问题。并发访问控制一直是多线程开发中的一个难点问题,如果没有精细的设计,比如说,只是简单地采用一个粗粒度互斥锁,就会出现不理想的结果:即使增加了线程,大部分线程也在等待获取访问共享资源的互斥锁,并行变串行,系统吞吐率并没有随着线程的增加而增加。而且,采用多线程开发一般会引入同步原语来保护共享资源的并发访问,这也会降低系统代码的易调试性和可维护性。

扫描二维码关注公众号,回复: 12898798 查看本文章

二、单线程 Redis 为什么那么快?

  • Redis 的大部分操作在内存上完成
  • 它采用了高效的数据结构,例如哈希表和跳表,这是它实现高性能的一个重要原因。
  •  Redis 采用了多路复用机制,使其在网络 IO 操作中能并发处理大量的客户端请求,实现高吞吐率。

1、基本I/O模型及其阻塞点

以 Get 请求为例,Redis为了处理一个 Get 请求,需要监听客户端请求(bind/listen),和客户端建立连接(accept),从 socket 中读取请求(recv),解析客户端发送请求(parse),根据请求类型读取键值数据(get),最后给客户端返回结果,即向 socket 中写回数据(send)。下图显示了这一过程,其中,bind/listen、accept、recv、parse 和 send 属于网络 IO 处理,而 get 属于键值数据操作。

既然 Redis 是单线程,那么,最基本的一种实现是在一个线程中依次执行上面说的这些操作。但是在这里的网络 IO 操作中,有潜在的阻塞点,分别是 accept() 和 recv()

  1. 当 Redis 监听到一个客户端有连接请求,但一直未能成功建立起连接时,会阻塞在 accept() 函数这里,导致其他客户端无法和 Redis 建立连接。
  2. 当 Redis 通过 recv() 从一个客户端读取数据时,如果数据一直没有到达,Redis 也会一直阻塞在 recv()。

这就导致 Redis 整个线程阻塞,无法处理其他客户端请求,效率很低。

2、非阻塞模式 

-- 音频06:49

Socket 网络模型的非阻塞模式设置,主要体现在三个关键的函数调用上,如果想要使用 socket 非阻塞模式,就必须要了解这三个函数的调用返回类型和设置模式。在 socket 模型中,不同操作调用后会返回不同的套接字类型。socket() 方法会返回主动套接字,然后调用 listen() 方法,将主动套接字转化为监听套接字,此时,可以监听来自客户端的连接请求。最后,调用 accept() 方法接收到达的客户端连接,并返回已连接套接字。

针对监听套接字,我们可以设置非阻塞模式:当 Redis 调用 accept() 但一直未有连接请求到达时,Redis 线程可以返回处理其他操作,而不用一直等待。但是,你要注意的是,调用 accept() 时,已经存在监听套接字了。虽然 Redis 线程可以不用继续等待,但是总得有机制继续在监听套接字上等待后续连接请求,并在有请求时通知 Redis。类似的,我们也可以针对已连接套接字设置非阻塞模式:Redis 调用 recv() 后,如果已连接套接字上一直没有数据到达,Redis 线程同样可以返回处理其他操作。我们也需要有机制继续监听该已连接套接字,并在有数据达到时通知 Redis。这样才能保证 Redis 线程,既不会像基本 IO 模型中一直在阻塞点等待,也不会导致 Redis 无法处理实际到达的连接请求或数据。

3、基于多路复用的高性能I/O模型

Linux 中的 IO 多路复用机制是指一个线程处理多个 IO 流,就是我们经常听到的 select/epoll 机制。

简单来说,在 Redis 只运行单线程的情况下,该机制允许内核中,同时存在多个监听套接字和已连接套接字。内核会一直监听这些套接字上的连接请求或数据请求。一旦有请求到达,就会交给 Redis 线程处理,这就实现了一个 Redis 线程处理多个 IO 流的效果。

注:2020 年 5 月,Redis 6.0 的稳定版发布了,Redis 6.0 中提出了多线程模型。那么,这个多线程模型和这节课所说的 IO 模型有什么关联?会引入复杂的并发控制问题吗?会给 Redis 6.0 带来多大提升?

猜你喜欢

转载自blog.csdn.net/u013025748/article/details/113542384