自顶向下redis4.0(3)命令与dict
redis4.0的命令
简介
正文
redisCommand与redisCommandTable
所有的命令一开始都位于server.c
文件起始位置的redisCommandTable
中,在观察redisCommandTable
表之前,我们先看一下redisCommand
结构体。
typedef void redisCommandProc(struct client *c);
typedef int *redisGetKeysProc(struct redisCommand *cmd, robj **argv, int argc, int *numkeys);
struct redisCommand {
char *name;
redisCommandProc *proc;
int arity;
char *sflags; /* Flags as string representation, one char per flag. */
int flags; /* The actual flags, obtained from the 'sflags' field. */
/* Use a function to determine keys arguments in a command line.
* Used for Redis Cluster redirect. */
redisGetKeysProc *getkeys_proc;
/* What keys should be loaded in background when calling this command? */
int firstkey; /* The first argument that's a key (0 = no keys) */
int lastkey; /* The last argument that's a key */
int keystep; /* The step between first and last key */
long long microseconds; /* total execution time */
long long calls;/*total execution count */
};
接着我们回到redisCommandTable
,此处仅展示其中的get
和set
命令。
struct redisCommand redisCommandTable[] = {
{"get",getCommand,3,"rF",0,NULL,1,1,1,0,0},
{"set",setCommand,-3,"wm",0,NULL,1,1,1,0,0}
};
name
字段为指令的名称,客户端发送请求第一个参数就是指令的名称,name
在字典中也被用于key
来找到对应的redisCommand
。
proc
字段为对应的处理函数,在解析完querybuf
里的参数后,redis
会执行c->cmd->proc(c)
调用对应的处理函数,以set
指令为例,在这里执行的就是setCommand
。下文会对get
和set
指令做详细的解析。
arity
字段遵循以下规则:1. 如果为正,那么指令拥有固定的参数个数。 2.如果为负,那么指令有着最小的参数个数,但参数个数可能会更多。 【参数个数包含指令名称自己】
比如 set
指令的最小参数个数为3,set msg "hello"
, 但指令参数可能会更多, SET anotherkey "will expire in a minute" EX 60
。
Command arity follows a simple pattern:
- positive if command has fixed number of required arguments.
- negative if command has minimum number of required arguments, but may have more.
Command arity includes counting the command name itself.
sflags
和flags
代表相同的标志,只是数据的表示形式不同,不同flag代表的意义如下:
- write - command may result in modifications
- readonly - command will never modify keys
- denyoom - reject command if currently out of memory
- admin - server admin command
- pubsub - pubsub-related command
- noscript - deny this command from scripts
- random - command has random results, dangerous for scripts
- sort_for_script - if called from script, sort output
- loading - allow command while database is loading
- stale - allow command while replica has stale data
- skip_monitor - do not show this command in MONITOR
- asking - cluster related - accept even if importing
- fast - command operates in constant or log(N) time. Used for latency monitoring.
- movablekeys - keys have no pre-determined position. You must discover keys yourself.
初始化命令
虽然初始化的工作大多数在redisCommandTable配置中已经完成,但还需要在initserverconfig
函数中会调用populateCommandTable
将sflags
中的字符值转化为flags
中的枚举值,并将命令注册到server.commands
字典中。
void populateCommandTable(void) {
int j;
int numcommands = sizeof(redisCommandTable)/sizeof(struct redisCommand);
for (j = 0; j < numcommands; j++) {
struct redisCommand *c = redisCommandTable+j;
char *f = c->sflags;
int retval1;
while(*f != '\0') {
switch(*f) {
case 'w': c->flags |= CMD_WRITE; break;
case 'r': c->flags |= CMD_READONLY; break;
case 'm': c->flags |= CMD_DENYOOM; break;
case 'a': c->flags |= CMD_ADMIN; break;
case 'p': c->flags |= CMD_PUBSUB; break;
case 's': c->flags |= CMD_NOSCRIPT; break;
case 'R': c->flags |= CMD_RANDOM; break;
case 'S': c->flags |= CMD_SORT_FOR_SCRIPT; break;
case 'l': c->flags |= CMD_LOADING; break;
case 't': c->flags |= CMD_STALE; break;
case 'M': c->flags |= CMD_SKIP_MONITOR; break;
case 'k': c->flags |= CMD_ASKING; break;
case 'F': c->flags |= CMD_FAST; break;
default: serverPanic("Unsupported command flag"); break;
}
f++;
}
retval1 = dictAdd(server.commands, sdsnew(c->name), c);
serverAssert(retval1 == DICT_OK );
}
}
执行命令
processCommand
函数在上篇文章中已经提到过,在解析完querybuf
中的字符串,将其转化为client->argc
和client->argv
中的值后会调用。
processCommand
本身做的工作大多数是条件检测,指令是否存在,参数个数是否合法,是否需要验证密码等等。
条件检测通过后,会调用call函数真正调用对应的处理函数。
set指令与字典
如果客户端向服务端发送set msg "hello"
请求,在进入到processCommand
指令的时候client->argc
会被设置为3,client->argv
数组会被填充对应的redisObject
。 lookupCommand
会使用client->argv[0]->ptr
也就是set
去server.commands
表中查找对应的redisCommand
。
server.commands
的类型是dict
,在服务器启动时初始化。
typedef struct dict {
dictType *type;
void *privdata;
dictht ht[2];
long rehashidx; /* rehashing not in progress if rehashidx == -1 */
unsigned long iterators; /* number of iterators currently running */
} dict;
创建一个dict
对象时会传入对应的dictType
对象,dictType
对象就是存储了几个函数指针的对象,redis
通过它实现了多态的效果。
typedef struct dictType {
uint64_t (*hashFunction)(const void *key);
void *(*keyDup)(void *privdata, const void *key);
void *(*valDup)(void *privdata, const void *obj);
int (*keyCompare)(void *privdata, const void *key1, const void *key2);
void (*keyDestructor)(void *privdata, void *key);
void (*valDestructor)(void *privdata, void *obj);
} dictType;
当创建command
表时,使用的是commandTableDictType
,这意味着当对一个key哈希的时候,使用的是dictSdsCaseHash
将会无视大小写,比较key值使用的函数是dictSdsKeyCaseCompre
。
参考文献
自顶向下redis4.0(3)命令与dict的更多相关文章
- 自顶向下redis4.0(4)时间事件与expire
redis4.0的时间事件与expire 目录 redis4.0的时间事件与expire 简介 正文 时间事件注册 时间事件触发 expire命令 删除过期键值 被动删除 主动删除/定期删除 参考文献 ...
- 自顶向下redis4.0(2)文件事件与客户端
redis4.0的文件事件与客户端 目录 redis4.0的文件事件与客户端 简介 正文 准备阶段 接受客户端连接 处理数据 返回数据结果 参考文献 简介 文件事件的流程大概如下: 在服务器初始化时生 ...
- 自顶向下redis4.0(1)启动
redis4.0的启动流程 目录 redis4.0的启动流程 简介 正文 全局server对象 初始化配置 初始化服务器 事件主循环 参考文献 简介 redis 在接收客户端连接之前,大概做了以下几件 ...
- 自顶向下redis4.0(5)持久化
redis4.0的持久化 目录 redis4.0的持久化 简介 正文 rdb持久化 save命令 bgsave命令 rdb定期保存数据 进程结束保存数据 aof持久化 数据缓冲区 刷新数据到磁盘 ap ...
- linux下redis4.0.2集群部署(利用Ruby脚本命令)
一.原生命令方式和Ruby脚本方式区别 利用Ruby脚本部署和用原生命令部署,节点准备的步骤都是一样的,节点启动后的握手,以及主从.槽分配,利用Ruby脚本一步就能完成,利用原生命令需要一步一步地执行 ...
- redis4.0.10安装与常用命令
----------- redis安装 ------------------------------------------- 安装reids:https://redis.io/download (4 ...
- redis-4.0.8 配置文件解读
# Redis configuration file example.## Note that in order to read the configuration file, Redis must ...
- Redis4.0新特性之-大KEY删除
接上一篇,我们得知了redis中存在大KEY,那么这个大KEY如何删除呢?本文将从源码角度分析Redis4.0带来的新特性. 在Redis中,对于大KEY的删除一直是个比较头疼的问题,为了不影响服务, ...
- Redis4.0新特性 -Lazy Free
Redis4.0新增了非常实用的lazy free特性,从根本上解决Big Key(主要指定元素较多集合类型Key)删除的风险.笔者在redis运维中也遇过几次Big Key删除带来可用性和性能故障. ...
随机推荐
- 使用大乌龟git和码云搭建版本库
刚刚过完清明,上班的第一天,大佬跟我说,要用码云和git搭建一个版本库,菜鸡的博主楞了半天,然后赶紧上网找资料,此时,一旁的大佬已经看不下去了,终于,出手了....... 1. 首先,先把大乌龟git ...
- Poem Codes - 攻防世界(Decrypt-the-Message)
Poem Codes Poem Code 最显著的特点就是一首诗歌. 详情请戳这里 让我们一起来过滤一遍这个神奇的加密过程~ ① 给出一首诗歌 for my purpose holds to sail ...
- Android RFID调试总结
调试了包括驱动,jni层,当然也熟悉了下应用层. 1. 驱动层包括修改: device/eastaeon/aeon6735_65c_l/init.project.rc //去 ...
- 使用ABBYY FineReader 14查看和编辑PDF
使用ABBYY FineReader,您可以轻松查看和编辑任何类型的 PDF,以及在其中添加注释和进行搜索,即使这些 PDF 是从扫描纸质文档生成.因而不包含任何可疑搜索或编辑的文本.是一款名副其实的 ...
- 理解与使用Treiber Stack
目录 背景 名称由来 CompletableFuture源码实现 FutureTask实现 Treiber Stack抽象实现 入栈 出栈 示例 参考 背景 最近在很多JDK源码中都看到了Treibe ...
- Vue3 Teleport
为什么需要 Teleport? 以 Dialog 组件为例,通常需要在一个组件中引入 Dialog 组件.然而,有时一部分逻辑属于 Dialog 所在的组件中,从技术角度来看,最好将这一部分移动到根节 ...
- pytest参数化
Pytest可以在多个级别上实现测试参数化 一.@pytest.fixture装饰器调用参数 示例 import pytest from selenium import webdriver from ...
- Linux中进程杀掉总是自动重启
<1> cat /proc/进程id/status 找到该子进程对应的父进程,将其父进
- 使用logisim搭建单周期CPU与添加指令
使用logisim搭建单周期CPU与添加指令 搭建 总设计 借用高老板的图,我们只需要分别做出PC.NPC.IM.RF.EXT.ALU.DM.Controller模块即可,再按图连线,最后进行控制信号 ...
- Contest 985
A 均移到黑色或白色即可. 时间复杂度 \(O\left(n\log n\right)\). B 枚举每种开关判断是否有灯只能靠该种开关控制. 时间复杂度 \(O\left(nm\right)\). ...