聊聊redis、epoll和多路IO复用之间的总总关系


theme: vue-pro

前言

带你一起聊聊企业中一般都怎么用的redis, 了解多路IO服用对redis的意义, 了解redis6.0为什么需要多线程, 了解 linux 中 select/poll和epoll的区别

本章内容

  • 介绍 redis 是什么?
  • 为什么使用redis?
  • redis在企业中都运用于哪些方面?
  • 多路IO复用
  • select/poll和epoll的区别是什么?
  • 为什么redis6.0需要多线程

是什么?

Redis是一个开源的内存数据结构存储系统,可以用作数据库、缓存、消息队列等多种用途。Redis支持多种数据结构,包括字符串、哈希、列表、集合和有序集合,提供了丰富的操作命令,支持分布式、事务、持久化等功能。

为什么?

Redis的优势在于:

  1. 高性能:Redis是内存存储系统,读写速度非常快,可以达到每秒数十万次的操作。此外,Redis的单线程架构可以避免多线程之间的锁竞争,提高性能。(至redis 6开始使用了多线程, 多线程技术在Redis中的应用主要体现在I/O线程和工作线程之间的分离。 I/O线程负责接收请求和发送响应,而工作线程则负责处理请求。这种架构的好处是可以充分利用多核CPU的性能,提高Redis的处理能力。)
  2. 数据结构多样化:Redis支持多种数据结构,可以满足不同的需求,例如:哈希可以存储对象,列表可以用来实现消息队列等。
  3. 分布式支持:Redis可以通过主从复制和集群来实现分布式,提高了系统的可用性和可扩展性。
  4. 丰富的功能:Redis支持事务、Lua脚本、发布订阅、过期时间等功能,可以满足复杂的应用场景。
  5. 社区活跃:Redis有一个活跃的开源社区,提供了大量的文档、工具和第三方库,方便开发人员使用和扩展。

Redis虽然有很多优点,但也有以下几个缺点:

  1. 数据持久化方案相对较弱:Redis的数据存储主要依赖于内存,如果遇到宕机等问题,数据可能会丢失。虽然Redis提供了多种数据持久化方案,如RDB、AOF等,但相对于传统的关系型数据库而言,仍存在一定的数据可靠性问题。
  2. 内存限制:由于Redis数据存储在内存中,因此受到服务器内存大小的限制。如果数据量过大,会导致Redis运行缓慢或崩溃。虽然可以使用集群来解决这个问题,但增加了系统复杂度。
  3. 复杂度较高:相对于一些简单的key-value存储系统,Redis的使用和配置比较复杂,需要一定的技术储备和经验。如果没有足够的了解和掌握,可能会出现一些问题。

虽然Redis存在一些缺点,但它的优点远大于缺点,因此在互联网领域被广泛应用。在选择Redis时,需要根据具体的应用场景和需求进行权衡和选择。

redis在企业中的用途都有哪些?

Redis在企业中的主要用途有以下几个方面:

  1. 缓存:Redis最常见的使用场景是作为缓存存储。由于Redis使用内存存储数据,读取速度非常快,可以大大提高系统的性能和响应速度。许多企业都将Redis用作分布式缓存系统,如京东、淘宝、美团等。
  2. 消息队列:Redis可以作为高性能的消息队列使用。通过Redis的发布/订阅机制,可以实现多个进程之间的实时消息传递和处理,常用于实时日志处理、实时数据同步等场景。
  3. 会话存储:企业应用程序通常需要存储用户会话信息,如登录状态、购物车信息等。Redis可以用作分布式会话存储,提供快速的会话管理和检索,可以大大提高系统的性能和扩展性。
  4. 数据库:Redis支持多种数据结构,包括字符串、哈希、列表、集合和有序集合等,可以作为键值数据库使用,存储和管理大量数据。
  5. 分布式锁:在分布式系统中,为了保证数据的一致性和避免竞态条件,通常需要使用分布式锁。Redis可以用作分布式锁实现工具,通过SETNX命令实现互斥锁。

除了以上几个方面,Redis还有其他一些应用场景,如地理位置信息存储、计数器和排行榜等。企业可以根据自己的业务需求和特点选择合适的Redis应用场景,并结合实际情况进行合理的部署和优化。

小白: 有个问题, redis6开始是不是更新了 多线程 么?

小黑: 是的

小白: 能介绍介绍么?

小黑: 介绍redis 6 的多线程改造, 先需要介绍什么是 多路 IO 复用

多路IO复用

是什么?

多路I/O复用(Multiplexing)是一种I/O模型,可以让一个进程同时监听多个I/O事件,从而实现异步I/O操作。

在多路I/O复用模型中,有一个统一的I/O事件管理器(如selectpollepoll等),进程将需要监听的I/O事件注册到该事件管理器中。当有一个或多个I/O事件就绪时,事件管理器会通知进程并返回就绪的I/O事件的列表,进程可以通过轮询或回调方式获取这些就绪的I/O事件,并进行相应的I/O操作。

多路I/O复用的主要优势在于可以让一个进程同时监听多个I/O事件,而不需要创建多个线程或进程,从而节省了系统资源,并提高了程序的可扩展性和性能。

在不同的操作系统上,多路I/O复用的实现方式有所不同。例如,在Linux中,可以使用selectpollepoll等系统调用实现多路I/O复用。在Windows中,可以使用IOCP(I/O Completion Ports)实现多路I/O复用。而在Java中,则可以使用NIO(New I/O)AIO(Asynchronous I/O)实现异步I/O操作,从而提高程序的性能和可伸缩性。

需要注意的是,多路I/O复用并不是万能的,它仅适用于那些I/O操作比较耗时的情况,如果I/O操作非常快速,则使用多路I/O复用可能会降低程序的性能。同时,多路I/O复用也需要谨慎使用,因为它会增加程序的复杂性和难度,需要仔细考虑设计和实现的方案。

小黑: 记住,IO复用是针对阻塞IO而言的,它不是多线程。它会将所有阻塞的IO操作罗列出来,形成一个无状态的状态机,就像红绿灯一样。根据灯的状态是绿灯、红灯还是黄灯,它会给出不同的处理结果,比如可写、可读和就绪。而灯的状态由用户层与底层socket的操作决定。比如,当用户层向底层socket写入数据时,灯的状态就会变成可写;当底层socket接受到其他socket客户端的信息时,事件管理器的文件描述符就会被修改为可读状态,然后用户层就可以读取这些数据。

多路IO复用的缺点是什么?

多路I/O复用(Multiplexing)的缺点主要包括以下几个方面:

  • 难以维护:多路I/O复用需要管理一个或多个事件集合,需要不断进行添加、删除、修改等操作,非常容易出错,且难以维护。

  • 实现复杂:多路I/O复用的实现相对于其他I/O模型而言比较复杂,需要考虑很多因素,例如事件管理器的设计、I/O事件的注册、就绪事件的处理等。

  • 不适用于高并发场景:在高并发场景下,多路I/O复用需要同时管理大量的I/O事件,而且需要不断地对这些事件进行检查和处理,这会导致事件管理器的负载过高,从而影响程序的性能。

  • 需要额外的资源:多路I/O复用需要额外的系统资源,例如事件管理器和事件集合等,这些资源需要占用一定的内存和CPU资源,可能会影响系统的稳定性和可靠性。

  • 需要配合其他技术使用:多路I/O复用不能单独使用,通常需要配合其他技术一起使用,例如线程池、异步I/O等,这会增加程序的复杂性和难度。

总的来说,多路I/O复用是一种高效的I/O模型,可以提高程序的性能和可扩展性。但是,在使用多路I/O复用时需要考虑其缺点,并根据具体的场景和需求选择合适的I/O模型。

能简单说下多路IO复用的过程么?

多路I/O复用的过程可以简单概括为以下几个步骤:

  • 调用系统调用(例如selectpollepoll)创建一个I/O事件集合,并向其中添加要监控的事件(例如文件描述符或者网络连接)。

  • 阻塞等待事件集合中的任意一个事件状态发生变化。

  • 当事件状态发生变化时,系统调用返回,并返回有变化的事件的列表。

  • 进程使用阻塞式I/O操作来处理有变化的事件。

  • 回到步骤1,继续监控事件的状态。

能举个生活中的例子说说多路IO复用么?

假设你是一家快递公司的快递员,每天需要在手机上同时接收和处理多个订单(I/O事件)。如果你使用阻塞式I/O操作来处理这些订单,那么你需要不断地切换手机屏幕,打开不同的订单页面,查询订单状态,然后再返回到手机主界面。这样会浪费大量的时间和精力,并且降低你的工作效率。

如果你使用多路I/O复用技术,你可以使用一个应用程序(系统调用)来监控多个订单的状态,并且在订单状态发生变化时,应用程序会立即通知你。这样你就可以在一个屏幕上同时查看和处理多个订单,而不需要不断地切换屏幕, 还可以做其他事情。这种方式可以大大提高你的工作效率和准确性,让你更好地完成快递配送工作。

select/pollepoll 的区别

小白: 很多人说, 多路IO复用不就是有一个线程在轮训事件状态么?

小黑: 这句话对了一半, 可以用 “select/pollepoll 的区别” 来解答这个问题

小黑: 在select机制下, 文件描述符是有限的, 好像只有1024个(但是它有个优先就是兼容性好, 在大多数linux设备上基本都支持select), 而 poll 机制打破了文件描述符的限制, 但是他们都存在那么一个线程在不断轮训收集事件状态, 即便这些事件根本就没有状态(也就是就绪状态)他也轮训一遍, 效率低下

小黑: 但如果是 epoll 的话, 就不一样了, 他会创建一个简易的文件系统, 不再依赖linux系统自带的fds文件描述符系统(linux自带的有数量限制), 将 select / poll 机制的步骤修改为下面三个步骤:

  • 调用epoll_create创建一个epoll实例,分配一个eventpoll结构体,该结构体包含了一个红黑树和一个就绪链表。(这就是一个简易的文件系统)
  • 调用epoll_ctlepoll实例中添加或删除需要监听的文件描述符,同时注册相应的事件和回调函数ep_poll_callback, 建立事件和系统网卡(或者其他IO设备)的回调关系。这些文件描述符会被插入到红黑树中,方便快速查找。
  • 调用epoll_wait等待事件发生。如果就绪链表为空,那么进程会被阻塞,直到有事件发生或超时。如果就绪链表不为空,那么进程会立即返回,并获取就绪链表中的事件(也就是将事件从内核态拷贝到用户态内存中)。

redis6.0的多线程改造

在前面我们了解了多路IO复用的详细过程, 你是否发现了一些问题

我们拿linux最好用的epoll为例, 分析分析, 它存在什么问题

epoll的过程大体上分为三个步骤, 创建, 添加和等待

创建基本上没什么问题, 借助了自己的文件系统, 打破linux文件描述符限制

添加也基本上没什么问题, 添加了事件, 文件描述符和回调函数, 同时和操作系统的IO设备进行了绑定

等待, 基本上大问题没有, 只监控就绪队列中的事件, 将继续队列处理完成的数据出内核态拷贝到用户态

等等, 从内核态拷贝到用户态, 这里都是一条线程进行的?

这里可能存在严重的问题啊, 结果发现,

就绪队列中存储的元素并不是socket接收到的数据,而是socket本身和可读或可写事件。epoll_wait函数只负责通知应用程序有数据可以读取或发送,但不负责读取或发送数据。

将接收数据的步骤转移给用户态的用户进行处理,是Reactor模式中的一种做法。在这种模式下,应用程序需要自己调用readwrite操作来完成数据的传输。

所以 redis6 的多线程改造并不是针对 epoll的就绪队列, 至少多线程功能并不是针对epoll的就绪队列事件从内核态复制到用户态的改造,而是在用户态使用多个IO线程来处理就绪队列事件

想天然了, linux早已对linuxepoll进行了更深层次的改造, 或者说是替换, 以支持Proactor模式为目的, redis6何苦再次改造?

也就是说, redis6多线程改造其实针对的是用户态的就绪队列, 而非内核态的就绪队列, 而内核并不负责socket的读取, 需要用户态的用户主动调用 writeread 进行读写操作, 而调用的过程就是 redis 多线程优化的过程

当一个socket准备好读取或写入时,内核会将其加入到epoll的就绪队列中。Redis中有一个专门的线程,称为IO线程,负责处理这个就绪队列中的事件。IO线程会调用epoll_wait函数等待事件的到来,当有事件到来时,IO线程会根据事件类型调用相应的处理函数进行处理。

具体来说,Redis的网络事件处理机制是这样的:

  1. 在主线程中调用aeCreateEventLoop函数创建事件循环。
  2. 在IO线程中调用aeCreateFileEvent函数将socket注册到事件循环中。
  3. socket准备好读取或写入时,内核将其加入到epoll的就绪队列中。
  4. IO线程在事件循环中调用epoll_wait函数等待事件的到来。
  5. 当有事件到来时,IO线程会根据事件类型调用相应的处理函数进行处理,如处理客户端的读写请求。
  6. 处理完成后,IO线程将处理结果返回给主线程。

总体上看, 就是Redis的IO线程不断地从epoll的就绪队列中取出事件,然后处理这些事件并返回结果。Redis的主线程只需要调用相应的API来注册和处理事件,不需要关心具体的事件处理过程。

所以网络上很多人说, redis 6.0多线程只处理:

  • write: 将数据从用户态写入内核态, 然后由内核借助网卡将信息发送出去
  • read: 将数据从网卡读取到内核态内存, 接着复制到用户态内存中

这样是不够的

它还可以:

  • 读取客户端请求数据并解析(解析你输入的指令)
  • 执行相应的命令
  • 将执行结果返回给客户端

至此, 在不断提出问题和解决问题的过程中, 我们已经了解到 redis6 为什么要上多线程了

至于redis多线程模型, 可以看 腾讯技术工程 的这篇文章 [Redis 多线程网络模型全面揭秘]

总结

这种文章真难写, 都大脑里出来的不知道是不是正确的, 当做一篇纯讨论文章吧

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值