Redis原理 - Redis网络模型

news2024/11/12 19:09:28

原文首更地址,阅读效果更佳!

Redis原理 - Redis网络模型 | CoderMast编程桅杆icon-default.png?t=N5K3https://www.codermast.com/database/redis/redis-netword-model.html

思考

Redis 到底是单线程还是多线程?

  • 如果仅仅针对 Redis 的核心业务部分(命令处理部分),则是单线程
  • 如果针对 Redis 整体,那么就是多线程

在 Redis 的版本迭代过程中,在两个重要的时间节点上引入了多线程的支持:

  • Redis v4.0:引入多线程异步处理一些耗时较长的任务,例如异步删除命令 unlike
  • Redis v6.0:在核心网络模型中引入多线程,进一步提高对多核 CPU 的利用率

为什么Redis要选择单线程?

  • 抛开持久化不谈,Redis是纯内存操作,执行速度非常快,它的性能瓶颈是网络延迟而不是执行速度,因此多线程并不会带来巨大的性能提升。
  • 多线程会导致过多的上下文切换,带来不必要的开销
  • 引入多线程会面临线程安全问题,必然要引入线程锁这样的安全手段,实现复杂度增高,而且性能也会大打折扣

#网络模型

Redis 通过 IO 多路复用来提高网络性能,并且支持各种不同的多路复用实现,并且将这些实现进行封装,提供了统一的高性能事件库 API 库 AE:

  • ae_epoll
  • ae_evport
  • ae_kqueue
  • ae_select(通用)

这是 Redis 中四种实现方式,根据不同的操作系统,选择不同的实现。

具体的 API 主要有以下几个:

  • aeApiCreate:创建多路复用程序,比如 epoll_create
  • aeApiResize
  • aeApiFree
  • aeApiAddEvent:注册 FD ,比如 epoll_ctl
  • aeApiDelEvent:删除 FD
  • aeApiPoll:等待 FD 就绪,比如 epoll_wait
  • aeApiName:select、poll

ae_evport 实现方式中独有的API

  • aeApiLookupPending
  • aeApiAssociate

在ae.c 文件中可以选择使用那种实现方式。

 

#单线程网络模型

Redis 6 以前的网络模型都是单线程的,Redis 单线程网络模型的整个过程:

 

  • 在 aeApiPoll 时,会判断是客户端可读还是服务端可读,调用不同的处理器
  • 当客户端 Client Socket 发起连接请求时,服务端 Server Socket 可读,触发连接应答处理器 tcpAccepthandler
  • 当客户端 Client Socket 发起命令时,客户端可读,触发命令请求处理器 readQueryFromClient
  • 当客户端可写时,会由命令回复处理器进行处理。

核心

本质上就是 IO 多路复用 + 事件派发 的应用。

server socket 不断接收 client socket 的响应,然后根据事件类型的不同,派发给对应的处理器进行处理。

#多线程网络模型

Redis 6.0 版本中引入了多线程,目的是为了提高 IO 读写效率。因此在 解析客户端命令、 写响应结果 时采用了多线程。核心的命令执行、IO 多路复用模块依然是由主线程执行。

通过对单线程网络模型的分析,主要的性能瓶颈在命令的读写处理和命令的响应输出两个方面。

 

故Redis 在命令读处理和命令的响应两个部分引入了多线程。

注意

性能的瓶颈一般情况下都是 IO 的影响或者 网络请求 的影响。

#底层实现

  • main

// server.c
int main(
    int argc,
    char **argv
){
    // ...
    // 初始化服务
    initServer();
    // ...
    // 开始监听事件循环
    aeMain(server.el);
    // ...
}
  • initServer

void initServer(void){
    // ...
    // 内部会调用 aeApiCreate(eventLoop),类似epoll_create
    server.el= aeCreateEventLoop(server.maxclients+CONFIG_FDSET_INCR);
    //...

    // 监听TCP端口,创建ServerSocket,并得到FD
    listenToPort(server.port,&server.ipfd)
    // ...

    // 注册 连接处理器,内部会调用 aeApiAddEvent(&server.ipfd)监听FD
    createSocketAcceptHandler(&server.ipfd,acceptTcpHandler)

    // 注册 ae_api_poll 前的处理器
    aeSetBeforeSleepProc(server.el,beforeSleep);
}


  • aeMain

void aeMain(aeEventloop*eventloop){
    eventLoop->stop = 0;
    // 循环监听事件
    while (!eventLoop->stop){
        aeProcessEvents(
            eventLoop,
            AE_ALL_EVENTS | 
            AE_CALL_BEFORE_SLEEP |
            AE_CALL_AFTER_SLEEP);
    }
}
  • aeProcessEvents

int aeProcessEvents(aeEventLoop *eventLoop,int flags){
    // ... 调用前置处理器 beforesleep
    eventLoop->beforesleep(eventLoop);
    // 等待FD就绪,类似 epoll_wait
    numevents = aeApiPoll(eventLoop,tvp);

    for (j = 0; j < numevents; j ++){
        // 遍历处理就绪的 FD,调用对应的处理器
    }
}
  • acceptTcpHandler

void acceptTcpHandler( ... ){
    // ...
    // 接收 socket 连接,获取 FD 
    fd = accept(s,sa,len);

    // ... 
    // 创建 connection ,关联 fd
    connection *conn = connCreateSocket();
    conn.fd = fd;

    // ...
    // 内部调用 aeApiAddEvent(fd,READABLE)
    // 监听 socket 的FD读事件,并绑定读处理器readQueryFromClient
    connSetReadHandler(conn, readQueryFromClient);
}
  • readQueryFromClient

void readQueryFromClient(connection *conn){
    // 获取当前客户端,客户端中有缓冲区用来读和写
    client *c = connGetPrivateData(conn);
    // 获取c->querybuf缓冲区大小
    long int qblen = sdslen(c->querybuf);
    // 读取请求数据到 c->querybuf 缓冲区
    connRead(c->conn,c->Guerybuf+qblen,readlen);
    // 解析缓冲区字符串,转为Redis命令参数存入 c->argv 数组
    processInputBuffer(c);
    // ...
    // 处理 c->argv 中的命令
    processCommand(c);
}
  • processCommand

int processCommand(client *c) {
    // 根据命令名称,寻找命令对应的command,例如 setCommand
    c->cmd = c->lastcmd = lookupCommand(c->argv[0]->ptr);
    // ...
    // 执行command,得到响应结果,例如ping命令,对应pingCommand
    c->cmd->proc(c);
    // 把执行结果写出,例如ping命令,就返回"pong"给cLient
    // shared.pong是 字符串"pong"的SDS对象
    addReply(c, shared.pong);
}
  • addReply

void addReply(client *c,robj *obj) {
    // 尝试把结果写到 c-buf 客户端写缓存区
    if (_addReplyToBuffer(c,obj->ptr,sdslen(obj->ptr)) != C_OK)
    // 如果c->buf写不下,则写到 c->reply,这是一个链表,容量无上限
    _addReplyProtoToList(c,obj->ptr,sdslen(obj->ptr));
    // 将客户端添加到server.clients_pending_write这个队列,等待被写出
    listAddNodeHead(server.clients_pending_ write,c);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/675515.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Vicuna-13B云服务器部署

Vicuna概述 Vicuna由一群主要来自加州大学伯克利分校的研究人员推出&#xff0c;仍然是熟悉的配方、熟悉的味道。Vicuna同样是基于Meta开源的LLaMA大模型微调而来&#xff0c;它的训练数据是来自ShareGPT上的7万多条数据&#xff08;ShareGPT一个分享ChatGPT对话的谷歌插件&am…

Geocomputation (3)Spatial data operations

Geocomputation &#xff08;3&#xff09;Spatial data operations 来源&#xff1a;https://github.com/geocompx/geocompy 1.准备 #| echo: false import pandas as pd import matplotlib.pyplot as plt pd.set_option("display.max_rows", 4) pd.set_option(&…

文件搜索引擎的搭建Elasticsearch+Fscrawler+SearchUI+Git+Nginx

文章目录 前言如何搭建文档搜索引擎服务器架构环境准备一、搭建Elasticsearch二、搭建Fscrawler三、搭建SearchUI服务四、定时拉取Git文件五、搭建Nginx文件下载服务器 前言 搭建一套文档搜索引擎。有时候&#xff0c;我们有一批文档&#xff0c;需要在这批文档中查找想要的内…

【从零开始学习JAVA | 第十篇】StringBuild介绍

前言&#xff1a; 本文会对StringBuild类进行详细的介绍&#xff0c;他相比较于String&#xff0c;在进行字符串拼接的时候大大提高了效率&#xff0c;是一个 很实用的工具。 StringBulider&#xff1a; StringBuilder是Java中的一个类&#xff0c;用于在一次创建和初始化后&a…

Java中反射机制,枚举,Lambda的使用

目录 一、反射机制 1、含义 2、作用 3、※反射相关的几个类 3.1、Class类&#xff08;Class对象是反射的基石&#xff09; 3.2、Class类中相关的方法 3.2.1 (※重要)常用获得类相关的方法 3.2.2 (※重要)常用获得类中属性、变量Field相关的方法 3.2.3 获得类中注解相…

详细设计报告

聊天系统设计与实现详细设计报告 1.编写目的 详细设计的主要任务是概要设计方案做完善和细化,本阶段主要对聊天应用系统进行过程化的描述,详细确定每一个功能模块的实现方式、执行流程,为程序员编码提供依据。设计用户界面。 2.总体方案确认 (1)系统总体结构确认 该项目管理…

Redis原理 - 通信协议RESP

原文首更地址&#xff0c;阅读效果更佳&#xff01; Redis原理 - 通信协议RESP | CoderMast编程桅杆https://www.codermast.com/database/redis/redis-communication-protocol.html RESP协议 Redis 是一个 CS 架构的软件&#xff0c;通信一般分两步&#xff08;不包括pipeli…

计算机组成与设计Patterson Hennessy 笔记_1 计算机概要与技术

Patterson & Hennessy 计算机概要与技术 计算机应用包括&#xff1a;个人计算机PC&#xff0c;服务器&#xff0c;嵌入式计算机。后PC时代出现了个人移动设备PMD&#xff08;手机&#xff09;&#xff0c;云计算&#xff08;在网络上提供服务的大服务器集群&#xff0c;供…

Unity Mac最新打苹果包流程

作者介绍&#xff1a;铸梦xy。IT公司技术合伙人&#xff0c;IT高级讲师&#xff0c;资深Unity架构师&#xff0c;铸梦之路系列课程创始人。 IOS详细打包流程1.申请APPID2.申请开发证书3.创建描述文件 IOS详细打包流程 1.申请AppID 2.创建证书 3.申请配置文件&#xff08;又名描…

NodeJS KOA⑩②

文章目录 ✨文章有误请指正&#xff0c;如果觉得对你有用&#xff0c;请点三连一波&#xff0c;蟹蟹支持&#x1f618;前言KOA Koa vs Express Koa更轻量 Koa~Context对象 Koa~异步流程控制 Koa~中间件模型Koa路由 1.1基本使用 2.2请求方式2.2.1规范写法2…

数据结构——带头节点的双向循环列表

带头节点的双向循环链表是一种特殊的双向链表&#xff0c;它与普通的双向链表相比&#xff0c;最大的区别是链表头结点的 next 指针不再指向第一个实际节点&#xff0c;而是指向链表中的第一个节点。同时&#xff0c;链表尾结点的 prev 指针也不再指向 NULL&#xff0c;而是指向…

轻松配置深度学习模型 ?

动动发财的小手&#xff0c;点个赞吧&#xff01; 由于所有模块都需要大量参数和设置&#xff0c;因此管理深度学习模型可能很困难。训练模块可能需要诸如 batch_size 或 num_epochs 之类的参数或学习率调度程序的参数。同样&#xff0c;数据预处理模块可能需要 train_test_spl…

Java接口幂等性,如何重试?

Java接口幂等性&#xff0c;如何重试&#xff1f; 文章目录 Java接口幂等性&#xff0c;如何重试&#xff1f;前言一、幂等性是什么&#xff1f;二、为什么要幂等性&#xff1f;三、使用什么办法实现幂等性&#xff1f;1.insert前先select2.加悲观锁3.加乐观锁4.加唯一索引5.Re…

uniapp系列-uni.getAppBaseInfo() versionCode appVersion 值不对应该怎么解决?

今天看到一个BUG 问题描述 我们使用uniapp的官方文档中uni.getAppBaseInfo()后获取的 appVersionCode appVersion &#xff0c;发现获得的结果和我们实际设置的不一致&#xff0c;不是manifest.json里面的值&#xff0c;如下图所示官方文档&#xff1a;https://uniapp.dcloud…

还原大师(MD5)

根据题目提示&#xff0c;都猜得到这应该跟MD5的加密形式有关系 我好像还没有具体了解过MD5编码的格式&#xff0c;或许本题可以通过MD5的编码格式推导出字符串 但是说实话&#xff0c;MD5的加密方式没有找到详细简介的文章 然后我就去网上百度了一下&#xff0c;经过大佬wp的洗…

java springboot整合MyBatis演示增删查改操作

前面我的文章 java springboot整合MyBatis做数据库查询操作讲述了整合springboot整合MyBatis 做了根据id查询的语句 那么 我们现在按它搭建的项目继续 我们在staffDao中添加一个insert函数 参考代码如下 Insert("insert into staff(name, age, status, departmentid) va…

chatgpt赋能python:Python编程实现1+22+333,解密方法

Python编程实现122333&#xff0c;解密方法 在Python编程开发中&#xff0c;我们经常需要求解不同类型的算数表达式&#xff0c;其中求解一系列类似122333的表达式是一个比较常见的需求。本文将会介绍如何使用Python语言方便地求解这类表达式&#xff0c;为大家提供一种针对此…

Redis中AOF和RDB

在Redis的持久化中&#xff0c;常使用的两个手段便是AOF和RDB进行持久化。 RDB&#xff08;Redis DataBase&#xff09;是Redis的持久化方式之一&#xff0c;在配置文件中&#xff0c;我们可以找到 对Redis进行持久化配置&#xff0c;而RDB在持久化时是怎么样进行工作的呢&…

ARM、ARM架构、ARM架构芯片

ARM是一种基于精简指令集&#xff08;RISC&#xff09;的处理器架构&#xff0c;它由英国的ARM公司设计和授权。 ARM芯片具有低功耗、高性能、高集成度等特点&#xff0c;广泛应用于嵌入式系统、移动设备、物联网、服务器等领域。本文将介绍ARM的各类芯片&#xff0c;包括其特…

Java-API简析_java.lang.ClassLoader类(基于 Latest JDK)(浅析源码)

【版权声明】未经博主同意&#xff0c;谢绝转载&#xff01;&#xff08;请尊重原创&#xff0c;博主保留追究权&#xff09; https://blog.csdn.net/m0_69908381/article/details/131345825 出自【进步*于辰的博客】 其实我的【Java-API】专栏内的博文对大家来说意义是不大的。…