Redis 实战之命令请求的执行过程

命令请求的执行过程

  • 发送命令请求
  • 读取命令请求
  • 命令执行器(1):查找命令实现
  • 命令执行器(2):执行预备操作
  • 命令执行器(3):调用命令的实现函数
  • 命令执行器(4):执行后续工作
  • 将命令回复发送给客户端
  • 客户端接收并打印命令回复
  • 总结

一个命令请求从发送到获得回复的过程中, 客户端和服务器需要完成一系列操作。

举个例子, 如果我们使用客户端执行以下命令:

redis> SET KEY VALUE
OK

那么从客户端发送 SET KEY VALUE 命令到获得回复 OK 期间, 客户端和服务器共需要执行以下操作:
1、 客户端向服务器发送命令请求SETKEYVALUE;
2、 服务器接收并处理客户端发来的命令请求SETKEYVALUE,在数据库中进行设置操作,并产生命令回复OK;
3、 服务器将命令回复OK发送给客户端;
4、 客户端接收服务器返回的命令回复OK,并将这个回复打印给用户观看;

发送命令请求

Redis 服务器的命令请求来自 Redis 客户端, 当用户在客户端中键入一个命令请求时, 客户端会将这个命令请求转换成协议格式, 然后通过连接到服务器的套接字, 将协议格式的命令请求发送给服务器,举个例子, 假设客户端执行命令:

SET KEY VALUE

那么客户端会将这个命令转换成协议:

*3\r\n$3\r\nSET\r\n$3\r\nKEY\r\n$5\r\nVALUE\r\n

然后将这段协议内容发送给服务器。

读取命令请求

当客户端与服务器之间的连接套接字因为客户端的写入而变得可读时, 服务器将调用命令请求处理器来执行以下操作:

1、 读取套接字中协议格式的命令请求,并将其保存到客户端状态的输入缓冲区里面;
2、 对输入缓冲区中的命令请求进行分析,提取出命令请求中包含的命令参数,以及命令参数的个数,然后分别将参数和参数个数保存到客户端状态的argv属性和argc属性里面;
3、 调用命令执行器,执行客户端指定的命令;

命令执行器(1):查找命令实现

命令执行器要做的第一件事就是根据客户端状态的 argv[0] 参数, 在命令表(command table)中查找参数所指定的命令, 并将找到的命令保存到客户端状态的 cmd 属性里面。

命令表是一个字典, 字典的键是一个个命令名字,比如 “set” 、 “get” 、 “del” ,等等; 而字典的值则是一个个 redisCommand 结构, 每个redisCommand 结构记录了一个 Redis 命令的实现信息, 表 14-1 记录了这个结构的各个主要属性的类型和作用。
在这里插入图片描述
在这里插入图片描述

命令名字的大小写不影响命令表的查找结果

因为命令表使用的是大小写无关的查找算法, 无论输入的命令名字是大写、小写或者混合大小写, 只要命令的名字是正确的, 就能找到相应的 redisCommand 结构。

比如说, 无论用户输入的命令名字是 “SET” 、 “set” 、 “SeT” 又或者 “sEt” , 命令表返回的都是同一个 redisCommand 结构。

这也是Redis 客户端可以发送不同大小写的命令, 并且获得相同执行结果的原因:

# 以下四个命令的执行效果完全一样redis> SET msg "hello world"
OKredis> set msg "hello world"
OKredis> SeT msg "hello world"
OKredis> sEt msg "hello world"
OK

命令执行器(2):执行预备操作

到目前为止, 服务器已经将执行命令所需的命令实现函数(保存在客户端状态的 cmd 属性)、参数(保存在客户端状态的 argv 属性)、参数个数(保存在客户端状态的 argc 属性)都收集齐了, 但是在真正执行命令之前, 程序还需要进行一些预备操作, 从而确保命令可以正确、顺利地被执行, 这些操作包括:

检查客户端状态的 cmd 指针是否指向 NULL , 如果是的话, 那么说明用户输入的命令名字找不到相应的命令实现, 服务器不再执行后续步骤, 并向客户端返回一个错误。
根据客户端 cmd 属性指向的 redisCommand 结构的 arity 属性, 检查命令请求所给定的参数个数是否正确, 当参数个数不正确时, 不再执行后续步骤, 直接向客户端返回一个错误。 比如说, 如果 redisCommand 结构的 arity 属性的值为 -3 , 那么用户输入的命令参数个数必须大于等于 3 个才行。
检查客户端是否已经通过了身份验证, 未通过身份验证的客户端只能执行 AUTH 命令, 如果未通过身份验证的客户端试图执行除 AUTH命令之外的其他命令, 那么服务器将向客户端返回一个错误。
如果服务器打开了 maxmemory 功能, 那么在执行命令之前, 先检查服务器的内存占用情况, 并在有需要时进行内存回收, 从而使得接下来的命令可以顺利执行。 如果内存回收失败, 那么不再执行后续步骤, 向客户端返回一个错误。
如果服务器上一次执行 BGSAVE 命令时出错, 并且服务器打开了 stop-writes-on-bgsave-error 功能, 而且服务器即将要执行的命令是一个写命令, 那么服务器将拒绝执行这个命令, 并向客户端返回一个错误。
如果客户端当前正在用 SUBSCRIBE 命令订阅频道, 或者正在用 PSUBSCRIBE 命令订阅模式, 那么服务器只会执行客户端发来的SUBSCRIBEPSUBSCRIBEUNSUBSCRIBEPUNSUBSCRIBE 四个命令, 其他别的命令都会被服务器拒绝。
如果服务器正在进行数据载入, 那么客户端发送的命令必须带有 l 标识(比如 INFOSHUTDOWNPUBLISH ,等等)才会被服务器执行, 其他别的命令都会被服务器拒绝。
如果服务器因为执行 Lua 脚本而超时并进入阻塞状态, 那么服务器只会执行客户端发来的 SHUTDOWN nosave 命令和 SCRIPT KILL 命令, 其他别的命令都会被服务器拒绝。
如果客户端正在执行事务, 那么服务器只会执行客户端发来的 EXECDISCARDMULTIWATCH 四个命令, 其他命令都会被放进事务队列中。
如果服务器打开了监视器功能, 那么服务器会将要执行的命令和参数等信息发送给监视器。

当完成了以上预备操作之后, 服务器就可以开始真正执行命令了。

注意

以上只列出了服务器在单机模式下执行命令时的检查操作, 当服务器在复制或者集群模式下执行命令时, 预备操作还会更多一些。

命令执行器(3):调用命令的实现函数

在前面的操作中, 服务器已经将要执行命令的实现保存到了客户端状态的 cmd 属性里面, 并将命令的参数和参数个数分别保存到了客户端状态的 argv 属性和 argc 属性里面, 当服务器决定要执行命令时, 它只要执行以下语句就可以了:

// client 是指向客户端状态的指针

client->cmd->proc(client);

因为执行命令所需的实际参数都已经保存到客户端状态的 argv 属性里面了, 所以命令的实现函数只需要一个指向客户端状态的指针作为参数即可。

对于这个例子来说, 执行语句:

client->cmd->proc(client);

等于执行语句:

setCommand(client);

被调用的命令实现函数会执行指定的操作, 并产生相应的命令回复, 这些回复会被保存在客户端状态的输出缓冲区里面(buf 属性和 reply属性), 之后实现函数还会为客户端的套接字关联命令回复处理器, 这个处理器负责将命令回复返回给客户端。

对于前面 SET 命令的例子来说, 函数调用 setCommand(client); 将产生一个 “+OK\r\n” 回复, 这个回复会被保存到客户端状态的 buf 属性里面。

命令执行器(4):执行后续工作

在执行完实现函数之后, 服务器还需要执行一些后续工作:

如果服务器开启了慢查询日志功能, 那么慢查询日志模块会检查是否需要为刚刚执行完的命令请求添加一条新的慢查询日志。
根据刚刚执行命令所耗费的时长, 更新被执行命令的 redisCommand 结构的 milliseconds 属性, 并将命令的 redisCommand 结构的 calls计数器的值增一。
如果服务器开启了 AOF 持久化功能, 那么 AOF 持久化模块会将刚刚执行的命令请求写入到 AOF 缓冲区里面。
如果有其他从服务器正在复制当前这个服务器, 那么服务器会将刚刚执行的命令传播给所有从服务器。

当以上操作都执行完了之后, 服务器对于当前命令的执行到此就告一段落了, 之后服务器就可以继续从文件事件处理器中取出并处理下一个命令请求了。

将命令回复发送给客户端

前面说过, 命令实现函数会将命令回复保存到客户端的输出缓冲区里面, 并为客户端的套接字关联命令回复处理器, 当客户端套接字变为可写状态时, 服务器就会执行命令回复处理器, 将保存在客户端输出缓冲区中的命令回复发送给客户端。

当命令回复发送完毕之后, 回复处理器会清空客户端状态的输出缓冲区, 为处理下一个命令请求做好准备。

客户端接收并打印命令回复

当客户端接收到协议格式的命令回复之后, 它会将这些回复转换成人类可读的格式, 并打印给用户观看(假设我们使用的是 Redis 自带的redis-cli 客户端)
继续以之前的 SET 命令为例子, 当客户端接到服务器发来的 “+OK\r\n” 协议回复时, 它会将这个回复转换成 “OK\n” , 然后打印给用户看:

redis> SET KEY VALUE
OK

以上就是 Redis 客户端和服务器执行命令请求的整个过程了。

总结

一个命令请求从发送到完成主要包括以下步骤: 1. 客户端将命令请求发送给服务器; 2. 服务器读取命令请求,并分析出命令参数; 3. 命令执行器根据参数查找命令的实现函数,然后执行实现函数并得出命令回复; 4. 服务器将命令回复返回给客户端。
serverCron 函数默认每隔 100 毫秒执行一次, 它的工作主要包括更新服务器状态信息, 处理服务器接收的 SIGTERM 信号, 管理客户端资源和数据库状态, 检查并执行持久化操作, 等等。
服务器从启动到能够处理客户端的命令请求需要执行以下步骤: 1. 初始化服务器状态; 2. 载入服务器配置; 3. 初始化服务器数据结构; 4. 还原数据库状态; 5. 执行事件循环。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【回溯算法】【Python实现】最大团问题

文章目录 [toc]问题描述回溯算法Python实现时间复杂性 问题描述 给定无向图 G ( V , E ) G (V , E) G(V,E),如果 U ⊆ V U \subseteq V U⊆V,且对任意 u u u, v ∈ U v \in U v∈U有 ( u , v ) ∈ E (u , v) \in E (u,v)∈E,则称…

Navicat导出表结构到Excel或Word

文章目录 sql语句复制到excel复制到Word sql语句 SELECTcols.COLUMN_NAME AS 字段,cols.COLUMN_TYPE AS 数据类型,IF(pks.CONSTRAINT_TYPE PRIMARY KEY, YES, NO) AS 是否为主键,IF(idxs.INDEX_NAME IS NOT NULL, YES, NO) AS 是否为索引,cols.IS_NULLABLE AS 是否为空,cols.…

13.Netty组件EventLoopGroup和EventLoop介绍

EventLoop 是一个单线程的执行器(同时维护了一个Selector),里面有run方法处理Channel上源源不断的io事件。 1.继承java.util.concurrent.ScheduledExecutorService因此包含了线程池中所有的方法。 2.继承netty自己的OrderedEventExecutor …

JDBC调用MogDB存储过程返回ref_cursor的方法和注意事项

MogDB在处理存储过程的时候,有时候需要返回结果集,类型为ref_cursor,但有时候可能会报错。而大部分应用程序都是使用Java JDBC. 根据我们这几年的数据库国产化改造经验,给大家分享一下JDBC调用 MogDB存储过程返回ref_cursor的方法…

算法设计与分析 例题解答 解空间与搜索

1.请画出用回溯法解n3的0-1背包问题的解空间树和当三个物品的重量为{20, 15, 10},价值为{20, 30, 25},背包容量为25时搜索空间树。 答: 解空间树: 搜索空间树: 2. 考虑用分支限界解0-1背包问题 给定n种物品和一背包…

2 GPIO控制

ESP32的GPIO的模式,一共有输入和输出模式两类。其中输入模式:上拉输入、下拉输入、浮空输入、模拟输入;输出模式:输出模式、开漏输出(跟stm32八种输入输出模式有所不同)。库函数中控制引脚的函数如下&#…

行业新应用:电机驱动将成为机器人的动力核心

电机已经遍布当今社会人们生活的方方面面,不仅应用范围越来越广,更新换代的速度也日益加快。按照工作电源分类,可以将它划分为直流电机和交流电机两大类型。直流电机中,按照线圈类型分类,又可以分为有铁芯的电机、空心…

FFmpeg常用API与示例(四)——过滤器实战

1.filter 在多媒体处理中,filter 的意思是被编码到输出文件之前用来修改输入文件内容的一个软件工具。如:视频翻转,旋转,缩放等。 语法:[input_link_label1]… filter_nameparameters [output_link_label1]… 1、视…

书生浦语训练营第四次课作业

基础作业 环境配置 拷贝internlm开发机内的环境 studio-conda xtuner0.1.17# 激活环境 conda activate xtuner0.1.17 # 进入家目录 (~的意思是 “当前用户的home路径”) cd ~ # 创建版本文件夹并进入,以跟随本教程 mkdir -p /root/xtuner0…

27.哀家要长脑子了!---栈与队列

1.739. 每日温度 - 力扣(LeetCode) 用单调栈的方法做: 从左到右遍历数组: 栈中存放的是下标,每个温度在原数组中的下标,从大到小排列,因为这样才能确保的是最近一天的升高温度 如果栈为空&am…

优化重启Redis服务脚本

#!/bin/sh echo -e "\033[33m正在关闭redis服务和测试redis进程是否关闭...\033[0m" pkill redis-server sleep 1 #echo -e "\033[33m测试redis进程是否关闭...\033[0m" ps -ef|grep redis-server sleep 1 echo "" echo -e "\033[33m正在…

【人工智能Ⅱ】实验5:自然语言处理实践(情感分类)

实验5:自然语言处理实践(情感分类) 一:实验目的与要求 1:掌握RNN、LSTM、GRU的原理。 2:学习用RNN、LSTM、GRU网络建立训练模型,并对模型进行评估。 3:学习用RNN、LSTM、GRU网络做…

2010年认证杯SPSSPRO杯数学建模D题(第一阶段)服务网点的分布全过程文档及程序

2010年认证杯SPSSPRO杯数学建模 D题 服务网点的分布 原题再现: 服务网点、通讯基站的设置,都存在如何设置较少的站点,获得较大效益的问题。通讯基站的覆盖范围一般是圆形的,而消防、快餐、快递服务则受到道路情况和到达时间的限…

【前端基础】CSS样式+Vue中绘制时间轴

深度选择器 在 Vue.js 中,/deep/、>>>、:deep 和 ::v-deep 这些都是深度选择器,用于修改子组件的样式。它们主要用于解决作用域样式和组件样式之间的冲突问题。 1. /deep/ 或 >>> /deep/ 和 >>> 是相同的选择器,…

华为机考入门python3--(23)牛客23- 删除字符串中出现次数最少的字符

分类:字符串 知识点: 访问字典中keychar的值,不存在则返回0 my_dict.get(char, 0) 字典的所有值 my_dict.value() 列表中的最小值 min(my_list) 题目来自【牛客】 import sysdef delete_min_freq_char(s):# 计算字母出现的频次…

将来会是Python、Java、Golang三足鼎立吗?

在开始前我有一些资料,是我根据网友给的问题精心整理了一份「 Java的资料从专业入门到高级教程」, 点个关注在评论区回复“888”之后私信回复“888”,全部无偿共享给大家!!! 软件工程里没有银弹&#xff…

word图片水印

一、word中旧水印如何删除 打开word模板,想要删除旧水印,如下图所示操作,但是旧水印删除不掉。 以为上传新水印图片会替换掉旧水印,结果显示了2个水印,要怎么删除呢? 如下截图所示,双击打开页…

雷森托尔环保科技有限公司见证2024杭州数字供应链装备展潮流

参展企业介绍 青岛雷森托尔环保科技有限公司创建于2018年,位于山东青岛,现注册资本3000万。公司主营生产模压木托盘、化工木托盘、大型设备木包装、出口木托盘、酒柜木酒架等,公司拥有技术人员6人,均为包装设计专业毕业&#xff0…

Qt | QSpinBox 类 QDoubleSpinBox 类(微调框)

01、QSpinBox 类 1、QSpinBox类是 QAbstractSpinBox 类的直接子类和具体实现, 2、QSpinBox 类被设计用于处理整数和离散值集合,对于浮点值使用 QDoubleSpinBox 类实现。 3、QSpinBox 默认只支持整数值,但可通过其内部的成员函数进行扩展,以支持使用不同的 字符串。 02…

【数组算法】598. 区间加法

给你一个 m x n 的矩阵 M 和一个操作数组 op 。矩阵初始化时所有的单元格都为 0 。ops[i] [ai, bi] 意味着当所有的 0 < x < ai 和 0 < y < bi 时&#xff0c; M[x][y] 应该加 1。 在 执行完所有操作后 &#xff0c;计算并返回 矩阵中最大整数的个数 。 示例 1: …