PlanExecutor:
总的来说是一个根据内部的执行树(Execution Tree)从数据库中获取数据的一个上层抽象结构/接口,在Query Engine执行find()
时就用这货获取数据,它还用于与查询优化器(Query Optimizer)或Cache等交互。
主要对外接口: 一系列的make()用于构建PlanExecutor. 服务于其本身的工厂设计模式,
- static StatusWith<std::unique_ptr<PlanExecutor, PlanExecutor::Deleter>> make(…) 构建一个PlanExecutor一般情况下需要CanonicalQuery(CQ),但不是必须的,比如Aggregate query可能创建个PlanExecutor用于获取数据,但不提供CQ
ExecState getNext(Document* objOut, RecordId* dlOut)
: 用于从数据库获取数据 ...
主要内部成员:
- WorkingSet: 是MongoDB用来存放于内存的数据工作集,例如当
PlanExecutor
在一步一步执行getNext()
过去数据的时候,获取的数据都是存入WorkingSet的。getNext()返回执行结果的同时返回给caller的RecordId* dlOut
正是用于从WorkingSet中获取数据的ID. - QuerySolution: 所用的Query Solution
- PlanStage: Query Solution对应的真正用于执行获取数据的数据结构。 ...
getExecutor() 主要流程:
Return value:
struct PrepareExecutionResult {
...
unique_ptr<CanonicalQuery> canonicalQuery; // 可能会被修改
// 查询树. 内部是一个查询树(节点为QuerySolutionNode),代表一个Query Plan, 可用于生成PlanStage tree.
unique_ptr<QuerySolution> querySolution;
// 对应Query Plan的 Query Execution Plan(QEP), 真正用于获取数据的底层数据结构。
unique_ptr<PlanStage> root;
};
主要流程: prepareExecution() -> PlanExecutor::make()
prepareExecution(): 为特定Query(canonicalQuery)构建一个执行树(Execution Tree).
1) 准备QueryPlannerParams(Query Planner需要的参数), 比如选择可用的indexes(过滤掉不可用的index,比如当用户设置了index filters
的时候,或者被用户隐藏的index(V4.4开始,用户可以隐藏index, 见SERVER-26589))
2) 准备IdHack Plan(当Query Planner决定使用_id
索引的时候)
3) 查Cache是否有Cached Plan可以用
4) QueryPlanner::plan()(产出备选query solutions/plans), 如果备选query solutions大于一个的话,则会有一个MultiPlanStage
作为最后Query Plan的root stage,该stage之后排序,选择最佳Query Solution用于该Query获取数据.(Link Token: [TODO],哪天有空再补个Mongo如何选择最佳query plan的笔记吧...)
5) Misc. 其他优化...
PlanExecutor::make()
: 构建PlanExecutor
...