当前位置: 首页 > news >正文

Redis的IO模型

Redis IO模型

Redis IO模型 使用的是基于 Reactor 模式的 I/O 多路复用模型。这个模型通过单线程事件循环来处理所有的客户端请求和响应。

基本模式

1. Reactor 模式

Reactor 模式是一种用于处理并发 I/O 操作的设计模式。它包含以下几个组件:

  • 多路复用器(Multiplexer):负责监听多个 I/O 事件,如读、写、连接等。
  • 事件处理器(Event Handler):处理特定事件的回调函数。

2. Redis 的 I/O 多路复用

Redis 使用操作系统提供的 I/O 多路复用机制,如 selectpollepoll 或 kqueue。这些机制可以在单个线程中监视多个文件描述符,以便在任何一个描述符准备好进行 I/O 操作时通知应用程序。

主要步骤:
  1. 事件循环初始化:创建和初始化事件循环及相应的多路复用器。
  2. 事件注册:将客户端套接字上的读写事件注册到多路复用器。
  3. 事件等待:调用多路复用器的方法,如 select 或 epoll_wait,等待事件发生。
  4. 事件分发:当事件发生时,多路复用器会返回就绪的文件描述符列表。
  5. 事件处理:根据事件类型调用相应的事件处理器(读事件、写事件等)。
  6. 响应客户端:将处理结果返回给客户端。

基本原理

Redis 的 I/O 模型实现原理,需要深入了解其事件驱动框架和多路复用机制。

1. 事件驱动框架

Redis 的事件驱动框架主要由以下几个组件组成:

  • 事件循环(Event Loop):这是事件驱动模型的核心,负责管理所有的 I/O 事件。
  • 事件类型(Event Types):Redis 主要处理两种事件:文件事件(File Events)和时间事件(Time Events)。
  • 事件处理器(Event Handlers):每个事件类型都有相应的事件处理器。

Redis 使用 aeEventLoop 结构体来管理事件循环,包含了文件事件和时间事件的注册、取消、处理等功能。

2. I/O 多路复用

Redis 使用操作系统提供的 I/O 多路复用机制,如 selectpollepoll(Linux)或 kqueue(BSD系统),这些系统调用允许一个线程同时监视多个文件描述符。

多路复用器(Multiplexer)

在 Redis 中,多路复用器通过 aeApi 结构体进行抽象,并根据操作系统的不同实现选择不同的多路复用策略:

  • ae_select.c:基于 select 系统调用。
  • ae_epoll.c:基于 epoll 系统调用。
  • ae_kqueue.c:基于 kqueue 系统调用。

3. Redis 的事件处理流程

Redis 的事件处理流程大致如下:

事件循环初始化
aeEventLoop *aeCreateEventLoop(int setsize) {aeEventLoop *eventLoop = zmalloc(sizeof(*eventLoop));// 初始化事件循环eventLoop->events = zmalloc(sizeof(aeFileEvent)*setsize);eventLoop->fired = zmalloc(sizeof(aeFiredEvent)*setsize);eventLoop->setsize = setsize;eventLoop->timeEventHead = NULL;eventLoop->timeEventNextId = 0;eventLoop->stop = 0;// 初始化多路复用APIif (aeApiCreate(eventLoop) == -1) {zfree(eventLoop->events);zfree(eventLoop->fired);zfree(eventLoop);return NULL;}return eventLoop;
}
事件注册
int aeCreateFileEvent(aeEventLoop *eventLoop, int fd, int mask,aeFileProc *proc, void *clientData) {if (fd >= eventLoop->setsize) return AE_ERR;aeFileEvent *fe = &eventLoop->events[fd];if (aeApiAddEvent(eventLoop, fd, mask) == -1)return AE_ERR;fe->mask |= mask;if (mask & AE_READABLE) fe->rfileProc = proc;if (mask & AE_WRITABLE) fe->wfileProc = proc;fe->clientData = clientData;return AE_OK;
}
事件等待
int aeApiPoll(aeEventLoop *eventLoop, struct timeval *tvp) {int retval, numevents = 0;retval = epoll_wait(eventLoop->apidata->epfd, eventLoop->apidata->events,eventLoop->setsize, tvp ? (tvp->tv_sec*1000 + tvp->tv_usec/1000) : -1);if (retval > 0) {int j;numevents = retval;for (j = 0; j < numevents; j++) {int mask = 0;struct epoll_event *e = eventLoop->apidata->events+j;if (e->events & EPOLLIN) mask |= AE_READABLE;if (e->events & EPOLLOUT) mask |= AE_WRITABLE;if (e->events & EPOLLERR) mask |= AE_WRITABLE;if (e->events & EPOLLHUP) mask |= AE_WRITABLE;eventLoop->fired[j].fd = e->data.fd;eventLoop->fired[j].mask = mask;}}return numevents;
}
事件处理
void aeProcessEvents(aeEventLoop *eventLoop, int flags) {int processed = 0, numevents;if (!(flags & AE_TIME_EVENTS) && !(flags & AE_FILE_EVENTS)) return;if (eventLoop->maxfd != -1) {numevents = aeApiPoll(eventLoop, tvp);for (j = 0; j < numevents; j++) {aeFileEvent *fe = &eventLoop->events[eventLoop->fired[j].fd];int mask = eventLoop->fired[j].mask;int fd = eventLoop->fired[j].fd;int rfired = 0;if (fe->mask & mask & AE_READABLE) {rfired = 1;fe->rfileProc(eventLoop,fd,fe->clientData,mask);}if (fe->mask & mask & AE_WRITABLE) {if (!rfired || fe->wfileProc != fe->rfileProc)fe->wfileProc(eventLoop,fd,fe->clientData,mask);}processed++;}}if (flags & AE_TIME_EVENTS)processed += processTimeEvents(eventLoop);return processed;
}

单线程的优势

  • 避免锁竞争:由于 Redis 运行在单线程中,所有操作都是原子的,不需要加锁机制来保护共享资源,简化了实现。
  • CPU 缓存友好:在单线程中,数据访问模式更加线性,减少了 CPU 缓存失效,提高了缓存命中率。
  • 性能:由于 Redis 的大部分操作是内存操作,并且操作系统的多路复用机制非常高效,单线程模型能够提供足够高的性能。
  • 原子性:单线程模型确保每个命令是原子执行的,不会出现数据竞争问题。

关于ArchManual

https://archmanual.com

https://github.com/yingqiangh/ArchManual

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 如何使用UWA Gears连接模拟器进行性能测试
  • Spring部分常见面试题
  • 记录k8s重启之后kubelet无法启动的问题
  • 数据库的实施过程分析
  • jeecg的单点登录
  • 如何使用YOLOv5进行物体检测,并通过GraspNet进行6D位姿估计,从而实现机械臂的抓取规划
  • misc音频隐写
  • 《代码整洁之道》读书笔记--目录
  • 【高级编程】synchronized 解决并发问题 类的线程安全类型
  • wireshark打开时空白|没有接口,卸载重装可以解决
  • iOS的传递链与响应链机制
  • CSP-J算法基础 树状结构与二叉树
  • 学习笔记 - 知识图谱的符号表示方法
  • C#中的装箱和拆箱是什么
  • Sentinel 控制界面
  • 【391天】每日项目总结系列128(2018.03.03)
  • 【跃迁之路】【585天】程序员高效学习方法论探索系列(实验阶段342-2018.09.13)...
  • Angular 响应式表单 基础例子
  • Laravel深入学习6 - 应用体系结构:解耦事件处理器
  • python学习笔记 - ThreadLocal
  • React的组件模式
  • unity如何实现一个固定宽度的orthagraphic相机
  • Vue.js源码(2):初探List Rendering
  • vue数据传递--我有特殊的实现技巧
  • 百度小程序遇到的问题
  • 从重复到重用
  • 无服务器化是企业 IT 架构的未来吗?
  • 项目管理碎碎念系列之一:干系人管理
  • 一道闭包题引发的思考
  • 宾利慕尚创始人典藏版国内首秀,2025年前实现全系车型电动化 | 2019上海车展 ...
  • 数据库巡检项
  • ​云纳万物 · 数皆有言|2021 七牛云战略发布会启幕,邀您赴约
  • #DBA杂记1
  • #控制台大学课堂点名问题_课堂随机点名
  • (1)(1.9) MSP (version 4.2)
  • (C语言)字符分类函数
  • (php伪随机数生成)[GWCTF 2019]枯燥的抽奖
  • (SERIES12)DM性能优化
  • (八)Docker网络跨主机通讯vxlan和vlan
  • (分布式缓存)Redis哨兵
  • (附源码)ssm基于jsp高校选课系统 毕业设计 291627
  • (六)Hibernate的二级缓存
  • (十七)Flink 容错机制
  • (学习日记)2024.01.19
  • (转)关于pipe()的详细解析
  • (转)拼包函数及网络封包的异常处理(含代码)
  • .bat批处理(二):%0 %1——给批处理脚本传递参数
  • .Net Core 中间件验签
  • .NET core 自定义过滤器 Filter 实现webapi RestFul 统一接口数据返回格式
  • .net mvc actionresult 返回字符串_.NET架构师知识普及
  • .net 怎么循环得到数组里的值_关于js数组
  • .NET/C# 如何获取当前进程的 CPU 和内存占用?如何获取全局 CPU 和内存占用?
  • .net开源工作流引擎ccflow表单数据返回值Pop分组模式和表格模式对比
  • @JSONField或@JsonProperty注解使用
  • @LoadBalanced 和 @RefreshScope 同时使用,负载均衡失效分析