首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >Redis内核级请求处理流程和原理

Redis内核级请求处理流程和原理

原创
作者头像
Ryan_小鱼
发布2025-07-13 16:36:19
发布2025-07-13 16:36:19
2370
举报

在一台Linux服务器上去部署一台Redis,启动这个Redis服务后,业务系统就可以通过redis-client来发送请求到这个Redis-server监听的这个端口号,这个其实就是基于TCP三次握手建立网络连接。

=> 连接成功后,在Redis-server就会生成一个对应的Socket,后续业务系统和Redis-server通信都是通过这对Socket来发送网络包

=> 在Redis-server会把业务系统发送过来的网络事件抽象成一个个文件事件,Redis-Server有文件事件处理器FileEventHandler来负责处理这些文件事件。

Redis的多路复用监听和文件事件模型

高并发场景下,会有多个redis-client和Redis-server建立网络连接。那么根据上面分析的,在Redis-server肯定会建立很多的socket来维护多个redis-client的联系

在Redis中也是引用了NIO的IO多路复用。就是FileEventHandler是以单线程的形式,同时去监听并处理多个socket产生的一些文件事件。而不是像BIO那样,就是一个socket连接,就要建立一个线程去监听。

在Socket中会有大量的网络事件,比如accpet连接应答,read和write事件,表示有数据可以写入和写出的事件。到了Redis这里,它就会负责把这些网络事件抽象成一个个文件事件,然后由FileEventHandler负责监听和处理这些文件事件

基于队列串行化的文件事件处理机制

给细化一下文件事件处理器把。就是一开始多个Socket产生的事件是Redis内部的IO多路复用程序来监听的。基于IO多路复用,同一时刻监听多个socket产生的事件,然后把这些有事件产生的socket一个个怼入到一个队列中,去串行化排队。基于FIFO先入队的Socket就先处理

由文件事件Dispatcher (文件事件分发器),根据socket产生的事件类型来负责把事件交给不同的处理器去处理。比如产生read事件的socket就交给命令请求处理器去处理。产生write事件的socket就交给命令响应处理器去处理...

Redis-Server完整的网络通信流程

当Redis-Server服务启动后,会有一个socket负责接收客户端发送过来的连接请求。然后由IO多路复用器负责监听这个socket产生的事件并把socket插入到对应的queue中

文件事件dispatcher根据socket产生的事件类型,把socket交给连接应答处理器去处理。连接应答处理器会通过一个监听连接socket和客户端通过TCP三次握手建立连接,建立连接成功后,就会重新建立一个socket,负责后续和客户端的socket互相通信,维护和客户端的联系。

成功建立连接后,就是客户端发送read请求,比如去读取Redis的某个key的value,接收请求的socket就会产生read事件,由IO多路复用程序监听并怼入到队列中,然后文件事件dispatcher负责把对应的事件交给命令请求处理器去处理。命令请求处理器会把到内存数据结构中执行请求

socket接收到read请求并处理后,会产生一个write事件,同样的流程,最终会给命令响应处理器去处理。之前read请求到内存数据结构后会返回一个响应,然后由命令响应处理器去接受这个响应,并根据产生write事件的socket把这个响应返回给客户端

Redis串行化单线程模型为什么能抗高并发?

Redis可以和大量的client建立连接,连接的建立,短时间内性能开销是可控的,而且Redis建立的连接是长连接,一旦建立连接就会长时间保持这个连接,避免短连接那样每次请求都要建立和断开连接。由于是长连接,第一次建立是要花费一定的时间的,后续这个连接就可以复用,不用频繁重新建立和断开连接

连接建立了之后,Redis就有大量的socket,客户端基于socket发送大量的请求过来,Redis的socket短时间内就有大量的网络事件,基于IO多路复用,由IO多路复用程序以单线程的方式,同时监听多个Socket的网络事件,避免了像BIO那样每监听一个socket就要创建一个线程,频繁的创建线程带来的性能损耗

IO多路复用器监听到文件事件后,会怼入到队列中执行串行化排队+单线程处理。这里由于是单线程,所以可以避免多线程并发访问内存数据结构,频繁的加锁和互斥,线程竞争带来的性能影响

还有一个点就是,多线程的上下文切换,对CPU的负载是很大的,CPU负载过高,会导致多线程执行的效率直线向下。

能不能抗高并发,要看每个请求执行和处理的速度和效率。一秒钟队列里面会有很多个请求,Redis是基于内存数据结构来操作的,单线程拿出一个请求去访问内存数据结构,耗时是非常低的,可能低于1ms,一秒钟内单线程可以很轻松的把几千个请求基于内存数据结构全部处理掉

Redis数据是基于内存存储的,不像MySQL这种关系型数据库这样,需要通过磁盘IO来读写数据。避免磁盘IO带来额外的性能损耗

Redis内核级请求处理流程和原理

Redis-Server服务启动后,会有一个连接监听的socket去监听端口,一旦发现redis-client发送连接请求,就会产生accept事件,就会被IO多路复用程序监听到,并把对应的socket怼入到队列中

文件事件dispatcher根据socket产生的事件,把它分配给连接应答处理器,会按照TCP三次握手建立连接,并在Redis-Server重新生成一个新的socket,负责和对应的redis-client通信,接收client发送过来的请求

连接成功后,Redis-Server会为client分配一块内存区域,作为缓冲区。可以分为输入缓冲区和输出缓冲区。当client发送请求读取数据的时候,socket会产生read事件,并最终分配到命令请求处理器

命令请求处理器就会负责把请求命令写入到输入缓冲区里面。输入缓冲区就会到redisCommend表,根据写入的请求命令,找到对应的rediscommend

rediscomment主要就是包括命令对数据处理的一些逻辑。找到对应的rediscommend后,会直接到内存数据结构去操作,并把响应返回给输出缓冲区

由于client发送read请求,Redis-Server的socket也会产生一个write事件,并分配给命令响应处理器,那么命令响应处理器就会到输出缓冲区获取响应结果,并根据之前dispatcher分配给它的那个socket,把结果输出到client,结束

Redis Client与Server网络通信协议分析

这个就是客户端和服务端之间需要统一网络协议,这样双方之间才能按照正常的格式去发送和解析数据。比如说,client 要发送什么样的数据给 server 端,以什么样的格式去发送数据,才能让 server 端可以理解我们想要干什么。

RedisClient.set(dfs, dsfdsf) -> 底层必须把这个命令和对应的 key value 的数据,组织成一条数据,通过网络发送过去。client 端发送的数据是这样组织的:REQUEST10568COMMANDsetKEYdfsVALUEdsfdsf。server 端需要去理解你的这条数据,他里面表达的是什么意思,如果不理解的话,server 端怎么来帮助我们请求的执行呢,不理解你要干什么

server 端期望的请求数据的格式是这样的:REQ10568CMDsetKEYdfsVALdsfdsf,他说我理解的请求数据的格式是这样的,我也希望按照这样的格式来进行请求数据解析和理解,结果呢,拿到的数据是这样的:REQUEST10568COMMANDsetKEYdfsVALUEdsfdsf ​ client 端发送的数据,到了 server 端手里的时候,是鸡同鸭讲,互相之间根本就不理解网络协议,协议是很关键,很重要的 。client 和 server 端之间数据是按照什么样的格式来进行组织的,能不能约定好一套相同的数据组织的格式,client 端按照和这个格式来组织请求数据,server端也按照同样的格式来处理数据,按照一套协议来进行通信,如何组织数据,数据组织的格式,就是一套网络通信协议 ​ SET key value -> jedis.set(key, value) -> 通过协议组织成*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$5\r\nvalue\r\n

Redis内核中的请求数据结构分析

网络通信的过程,一般客户端发送给redis server的请求数据,比如说代码里的对象或者数据结构,按照redis网络通信协议组织后,会变成*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$5\r\nvalue\r\n这么一串东西,这么一串协议数据就会先被序列化成字节数据流

=> 就是先把数据转换成byte[]数组,再去通过socket,使用网络去传输这个字节输入流。以字节输入流的方式,把数据传输到server端

=> server端就是接收到这么一串字节数组后,会通过反序列化的方式把它还原成之前的那串协议数据,比如说*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$5\r\nvalue\r\n这么一串东西

=> client端和server端建立连接后,会在server端建立一个RedisClient的一个内存数据结构,一个客户端就对应一个RedisClient。多个client和server端建立连接,在server端就会通过链表的方式来管理这些RedisClient

=> server端接收并反序列化客户端发送过来的协议数据后,找到这个client在server端对应的RedisClient。然后会把数据存储到客户端对应的RedisClient里面的queryBuf。这里queryBuf可以理解为就是RedisClient的输入缓冲区

=> 这个queryBuf底层会维护sdshdr这样的一个数据结构, 里面会有一个free 0,代表当前空闲的空间。还有一个len 33,代表的是输入缓冲区里面缓存的数据有多长。在这个sdshdr里面还有一个buf,这里才是存放客户端发送过来的数据,比如说*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$5\r\nvalue\r\n这么一串东西

=> 然后server端就可以通过网络通信协议去解析这串字节数组了,解析后就会把对应的请求命令存放在输入缓冲区中的一个argv里面,后续就基于argv里面的命令和参数到rediscommend表里面,对内存数据结构进行操作

Redis内核中的命令函数查找和执行

前面讲到,就是输入缓冲区的数据反序列化后,基于通信协议解析,最后在输入缓冲区的argv变量存储着基于网络协议解析后的请求命令和参数

在server端会有一个命令查找表,记录着redis命令对应的rediscommend的引用,从argv获取到SET命令,就可以知道这个命令指向的rediscomend是哪一个,这个rediscommend里面记录的是redis命令底层对内存数据结构的操作逻辑。

输入缓冲区除了argv还有一个变量cmd,用来指向rediscommend,也就是说当前cmd指针指向的是SET命令对应的rediscommend这样的命令函数,然后再通过调用命令函数把key和value的值传递过去执行

执行完毕后rediscommend会把响应写回到输出缓冲区,在输出缓冲区里面会有一个buf,里面记录的就是rediscommend返回的响应结果,比如说+OK/r/n0...这样子

最终这个响应结果返回给命令响应处理器,由它通过序列化成字节输入流,通过socket返回给客户端,客户端再通过反序列化,基于统一的网络协议去解析这段响应数据

补充说明

redis命令查找表有指向rediscommend的指针,为什么还要有cmd指针?

Redis 的命令查找表和客户端结构中的 cmd 指针服务于不同的设计目的,二者协同工作以确保命令执行的高效性和状态管理的清晰性

  1. 命令查找表(redisServer->commands)的作用‌ 这是一个全局的命令字典,存储 Redis 支持的所有命令的元信息。它以命令名称(如 "SET""GET")为键,对应的 redisCommand 结构体为值。 ‌核心功能‌:当客户端发送一个命令请求时,服务器需要根据命令名称‌查找‌对应的命令实现(即 redisCommand 结构)。这个查找操作是命令执行的起点。
  2. cmd 指针(在 redisClient 结构中)的作用‌ 当服务器在全局命令表中查找到对应命令的 redisCommand 结构后,会将该结构的‌指针‌赋值给代表当前客户端连接状态的结构体(redisClient)中的 cmd 成员。 ‌核心功能‌:
    • 避免重复查找‌:一旦在命令解析阶段查找到正确的 redisCommand 并将其指针存储在 client->cmd 中,后续的命令执行阶段(调用 proc 函数指针)和可能的后续处理(如写 AOF、传播给从节点)可以直接使用 client->cmd 访问命令信息,无需再次在全局命令表中查找,极大提升了效率。
    • 关联命令与当前客户端状态‌:redisClient 结构体包含了当前客户端连接的所有状态信息(如 socket 描述符 fd、查询缓冲区 querybuf、命令参数 argv/argc、回复缓冲区 reply 等)。将 cmd 指针存储在 redisClient 中,使得命令的执行函数(proc)可以方便地通过 client 参数同时访问到‌命令的实现细节(client->cmd->proc)‌ 和‌执行该命令所需的客户端上下文信息(client->argc, client->argv 等)‌。
    • 明确当前执行的命令‌:在处理一个客户端连接的过程中(特别是处理管道命令时),client->cmd 清晰地标识了服务器当前正在为该客户端执行的是哪一个具体的命令,简化了状态跟踪和管理。

    总结‌ 二者的关系与分工: 命令查找表 (redisServer->commands) 是‌全局的、静态的命令注册中心‌,负责提供命令名称到命令实现的映射。 redisClient 结构中的 cmd 指针是‌动态的、与特定客户端连接绑定的‌,它指向当前正在为该客户端执行的命令的 redisCommand 结构。它避免了每次命令执行时重复查询全局表,并将命令实现与执行该命令所需的客户端上下文紧密关联在一起,是 Redis 高效处理命令的关键设计之一

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Redis的多路复用监听和文件事件模型
  • 基于队列串行化的文件事件处理机制
  • Redis-Server完整的网络通信流程
  • Redis串行化单线程模型为什么能抗高并发?
  • Redis内核级请求处理流程和原理
  • Redis Client与Server网络通信协议分析
  • Redis内核中的请求数据结构分析
  • Redis内核中的命令函数查找和执行
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档