概述
SSDB 使用 LevelDB 存储数据,读写数据直接使用 LevelDB 接口实现。在网络时间处理方面使用读/写事件线程池提高处理性能,优先使用 epoll 作为事件处理器,否则降级为 select。
一 网络事件处理
在 SSDB 中将事件抽象为 Fdevent
类型,时间处理抽象为 Fdevents
。在 Fdevents
中使用 vector
保存需要监听的全部 Fdevent
事件(events 中保存)、以及已经触发的 SSDB 指令事件(ready_events 中保存)。
事件处理循环读起来有些混乱,在整个循环的前部分将需要处理的事件放入 ready_events
中,后半部分将 ready_events
中的事件匹配对应的 Redis 指令处理函数,并将其放入读/写事件处理线程池中。在线程池中,会将指令的处理结果发送给客户端。
[ssdb]/src/net/server.cpp:serve()
事件处理主体
[ssdb]/src/net/server.cpp:serve()
const Request *req = link->recv();
:指令解析int result=this->proc(job);
:将解析出来的指令作为任务添加到线程池中job->cmd = proc_map.get_proc(req->at(0));
:设置指令对应的处理函数指针
二 指令处理
在读/写线程池中,实际的指令处理函数为 int ProcWorker::proc(ProcJob *job)
。所有的函数指针在 SSDBServer 对象创建阶段进行注册(通过 reg_procs 函数进行注册)。根据注册函数表可以找到对应的处理函数,例如注册 REG_PROC(get, "rt")
找到处理函数为 proc_get
,即可以跟踪指令处理。
1. set 指令处理
函数调用顺序:proc_set() => serv->ssdb->set() => binlogs->Put()/add_log()/commit()
从上面的调用流程可以看出数据先保存在 binlog 中(LevelDB)实现。
2. get 指令处理
函数调用顺序:proc_get() => serv->ssdb->get() => ldb->Get()
为什么在 set
指令中将数据保存在 binlog 中,而 get
指令却在 ldb 中进行数据查找?其实 binlog 底层使用 leveldb 进行数据存储,其使用的数据文件即 SSDBImpl::ldb 成员。
1 | SSDB* SSDB::open(const Options &opt, const std::string &dir){ |