Pika命令处理流程源码分析

背景

我们希望通过阅读 Pika 的源码来熟悉 Pika 接收到一个命令时怎么进行处理的

Pika执行命令流程

src/pika_client_conn.cc

截屏2023-07-05 19.35.43.png

Pika 执行命令的流程从 PikaClientConn::Docmd 这个函数开始,这个函数的三个参数 argv 里面存命令的参数,是一个 vector,比如一条 set 命令(examlpe:argv[0] 为 set,argv[1] 为 key,argv[2] 为 value),第二个参数 opt 为命令名称,第三个参数 resp_ptr 是一个指向 string(reply) 的指针,这里的 g_pika_cmd_table_manager->GetCmd(opt) 函数查询 Cmd 中是否有存在的命令,如果没有则 return,对于返回值不为空的 c_ptr,我们会执行 SetConn 和 SetResp 操作,给 conn_ 和 resp_ptr 初始化.

截屏2023-07-05 19.43.02.png

截屏2023-07-05 19.45.01.png

这里的 c_ptr->Inital 是对命令进行初始化操作,各个命令覆写了 Cmd 的 Initial,以 Get 命令为例,执行 DoInitial一般是对命令传进来的参数数量判断是否合理.接下来的 UpdageQueryNumAndExecCountDB 是对 Qps 等信息进行统计.

截屏2023-07-05 19.49.19.png

c_ptr->Executer() 是执行命令的函数,下面我们可以看下这个函数里面的具体逻辑

src/pika_command.cc

截屏2023-07-05 19.51.32.png

在 Execute 函数里面有几层逻辑,我们以 Del 命令举例,我们会走到最后一层逻辑,这里是根据 cmd 名字来走不同的逻辑,接着我们继续看下 ProcessSingleSlotCmd() 这个函数.

截屏2023-07-05 19.54.09.png

在 ProcessSingleSlotCmd 里面,通过 GetSlotByDBName和GetSyncMasterSlotByname 获取 slot 和sync_slot,然后执行 ProcessCommand

截屏2023-07-05 19.55.58.png

我们以 Del 命令为例,会走到 InternalProcessCommand 这层逻辑.

截屏2023-07-05 19.57.29.png

截屏2023-07-05 20.03.31.png

在 InternalProcessCommand 里面,通过加锁 record_lock 防止多并发情况下对同一个 key 进行写请求,即单线程安全.程序首先执行 DoCommand,DoCommand 中有个 Do(slot) 函数,每个子命令覆写了 Do 函数,对于写操作的命令,我们会执行 DoBinlog。以下我以 Del 命令为例讲解:

src/pika_kv.cc

截屏2023-07-05 20.06.50.png

每个命令都有自己的 Do 方法,这里的是 Del 命令为例的执行情况,我们可以继续往下看看这个 Del 指令的具体执行逻辑

截屏2023-07-05 20.11.19.png

可以看到这里就是存储引擎 Storage(Blackwidow) 的处理流程,对于 del 命令,对 5 种数据结构下的某一个 key进行删除并计数最终返回一个 count.这里我们可以继续对某一个数据结构比如 string 来看具体这个 Del 做了什么操作. 图中的 string_db_ 是 std::unique_ptr<Redisstrings>类型。

截屏2023-07-05 20.15.14.png

这里我们可以看到,首先我们会先 Get 一下这个 key 看是否存在,如果存在我们继续执行if里面的逻辑,我们可以继续看下 ParsedStringsValue 这层逻辑

截屏2023-07-05 20.20.49.png

可以发现这里的 internal_value_str 就是数据库里面要删除的那个 Key 对应的 value,这里是对过期时间进行了设置,我们对 db_->Delete 继续往下看

pika/deps/include/rocksdb/db.h

截屏2023-07-05 20.30.42.png

这里就是调用 RocksDB 层的函数 Delete 了

src/pika_command.cc

截屏2023-07-05 20.39.31.png

看完命令执行后我们继续看下 DoBinlog 这个函数

截屏2023-07-05 20.41.15.png

这里会判断是否是写命令,如果是的话,则进行 DoBinlog 操作,可以看到这个 DoBinlog 是父类方法为子类用