输入关键词,开始搜索

↑↓ 选择↵ 打开esc 关闭
沉浸

七大主流数据库查询执行流程详解:从 MongoDB 到 NebulaGraph

10 min read

理解一个数据库的查询执行流程,是掌握其性能特性和选型依据的关键。不同数据库面向的场景不同,其查询执行的模块划分、数据流转方式也截然不同。

本文选取七个具有代表性的数据库系统,覆盖文档数据库、搜索引擎、关系型数据库、OLAP 数据库、向量数据库和图数据库,逐一拆解一条查询从客户端发起到结果返回的完整路径。


一、MongoDB:文档行存 + B+Tree 索引

MongoDB 的查询执行流程相对经典,和 MySQL 类似,但在存储格式和聚合能力上有自己的特色。

执行流程

Client  │  ▼┌─────────────┐│  mongos     │  ← 分片集群时做路由(Standalone 跳过)│  (路由器)    │└──────┬──────┘       │       ▼┌─────────────┐     ┌─────────────┐│  Parser     │────▶│  Optimizer  │  ← 生成多个候选计划,选成本最低的│  (查询解析)  │     │  (计划选择)  │└──────┬──────┘     └──────┬──────┘                          │                          ▼┌─────────────────────────────────┐│           Execution             ││  ┌─────────┐    ┌─────────┐    ││  │  Index  │ 或  │ CollScan│    │  ← 走索引还是全表扫描│  │  Scan   │    │ (全扫描) │    ││  └────┬────┘    └────┬────┘    ││       │              │          ││       └──────┬───────┘          ││              ▼                  ││  ┌─────────────────────────┐    ││  │  WiredTiger 存储引擎     │    │  ← B+Tree 索引、Page Cache、磁盘 IO│  │  - Cache (内存缓存)      │    ││  │  - CheckPoint (磁盘数据) │    ││  └─────────────────────────┘    │└─────────────────────────────────┘       │       ▼    Result

涉及的核心模块

模块职责
mongos分片集群的路由器,将查询分发到正确分片
Parser解析查询语句(find、aggregate 等),生成内部表示
Optimizer基于统计信息生成多个候选计划,选择成本最低的
Execution Engine执行查询计划,支持单文档查询和聚合管道
WiredTiger存储引擎,负责 B+Tree 索引维护、Page Cache、磁盘 IO

特色说明

MongoDB 的聚合管道(Aggregation Pipeline)是其查询能力的核心。一个聚合查询会按阶段(Stage)依次执行,如 $match -> $project -> $group -> $sort,每个阶段处理上一个阶段的输出。这种设计让 MongoDB 在文档数据库中具备了不错的分析能力,但大规模聚合仍是其短板。


二、Elasticsearch:两阶段查询 + 倒排索引

Elasticsearch 的查询执行和数据库有本质区别。它的核心不是”扫描数据”,而是”通过倒排索引定位文档”。

执行流程

Client  │  ▼┌─────────────────┐│ Coordinator Node │  ← 接收查询,协调分发└────────┬────────┘         │    ┌────┴────┐    ▼         ▼Shard 0   Shard 1 ...  ← 每个分片并行执行【第一阶段:Query Phase】每个 Shard 内部:┌────────────────────────────────────────┐│              Lucene Segment             ││  ┌─────────────┐    ┌─────────────┐    ││  │ Inverted Idx│    │  Doc Values │    ││  │  (定位文档)  │    │  (聚合排序)  │    ││  └──────┬──────┘    └─────────────┘    ││         │                               ││         ▼                               ││  ┌─────────────────────────────┐        ││  │  评分 (BM25/TF-IDF)          │        ││  │  + 收集 Top-K 文档 ID         │        ││  └─────────────────────────────┘        │└────────────────────────────────────────┘         │         ▼    返回局部 Top-K 给 Coordinator         │         ▼┌─────────────────────────────┐│ Coordinator 合并全局 Top-K   │  ← 跨分片排序取 Top N└─────────────┬───────────────┘              │【第二阶段:Fetch Phase】              │    ┌─────────┴─────────┐    ▼                   ▼Shard 0             Shard 1┌─────────────┐     ┌─────────────┐│  _source     │     │  _source     │  ← 根据文档 ID 拉回完整 JSON│  (行存文档)   │     │  (行存文档)   │└─────────────┘     └─────────────┘              │              ▼         返回完整结果

涉及的核心模块

模块职责
Coordinator Node接收客户端查询,分发给各分片,合并结果
Lucene底层搜索引擎库,提供倒排索引、评分、聚合能力
SegmentLucene 的不可变索引段,是查询的最小单位
Translog写入事务日志,保证数据不丢失
Doc Values列存结构的字段值存储,支撑聚合和排序

特色说明

ES 的两阶段查询是其核心设计:

  • Query Phase:各分片通过倒排索引找出匹配的文档 ID,按评分排序返回局部 Top-K。这个阶段不读完整的文档内容。
  • Fetch Phase:Coordinator 合并全局 Top-K 后,向相关分片请求完整的 _source 文档。

这种设计的代价是:如果查询需要聚合大量文档(如统计全量数据的平均值),ES 需要在 Query Phase 收集大量文档 ID,性能会下降。此时 Doc Values(列存副本)就派上用场了——聚合查询直接读 Doc Values,不经过两阶段流程。


三、PostgreSQL:经典火山模型 + 成本优化器

PostgreSQL 是开源关系型数据库中查询执行流程最清晰的代表,其 Planner 和 Executor 的设计影响了大量后续系统。

执行流程

Client  │  ▼┌─────────┐│  Parser │  ← SQL 词法/语法解析,生成 Parse Tree└────┬────┘     │     ▼┌───────────┐│  Analyzer │  ← 语义分析:表/列是否存在、类型检查└─────┬─────┘      │      ▼┌───────────┐│  Rewriter │  ← 视图展开、规则重写└─────┬─────┘      │      ▼┌─────────────────────────────────────────┐│           Planner / Optimizer            ││  ┌─────────────────────────────────┐    ││  │  生成候选执行计划                 │    ││  │  - Seq Scan / Index Scan        │    ││  │  - Join (NestedLoop/Hash/Merge) │    ││  │  - Sort / Aggregate             │    ││  └─────────────────────────────────┘    ││                   │                     ││  ┌────────────────┘                     ││  ▼                                      ││  成本估算(基于 pg_stat 统计信息)         ││  选择最低成本计划                         │└─────────────────────────────────────────┘      │      ▼┌─────────────────────────────────────────┐│              Executor                    ││  火山模型(Volcano,逐行处理)            ││  或向量化(PG 14+,批量处理)             ││                                         ││  ┌──────────┐    ┌──────────┐          ││  │ SeqScan  │───▶│  HashJoin│          ││  │IndexScan │    │   ...    │          ││  └────┬─────┘    └────┬─────┘          ││       │               │                 ││       └───────┬───────┘                 ││               ▼                         ││  ┌─────────────────────────────────┐    ││  │       Buffer Manager            │    ││  │  Shared Buffer (共享内存缓存)    │    ││  │  ┌─────────┐    ┌────────────┐ │    ││  │  │  Heap   │    │   Index    │ │    ││  │  │ (表数据)│    │ (B+Tree等) │ │    ││  │  └────┬────┘    └────────────┘ │    ││  │       │                         │    ││  │       ▼                         │    ││  │  磁盘文件(Page 粒度读取)        │    ││  └─────────────────────────────────┘    │└─────────────────────────────────────────┘      │      ▼   Result

涉及的核心模块

模块职责
ParserSQL 词法/语法解析
Analyzer语义分析,检查表/列存在性、类型匹配
Rewriter视图展开、规则系统重写查询
Planner基于成本的优化器(CBO),生成并选择最优执行计划
Executor火山模型执行引擎(可扩展为向量化)
Buffer Manager管理 Shared Buffer(共享内存缓存),协调多进程访问
WAL预写日志,保证事务持久性
Access MethodsTable AM / Index AM 接口,支持自定义存储引擎

特色说明

PostgreSQL 的 Planner 是其最精华的部分。它维护了一套详细的统计信息(pg_stat 系统表),包括每个表的行数、每列的数据分布直方图、相关性等。优化器基于这些统计信息估算不同执行计划的代价(磁盘 IO、CPU、网络),选择成本最低的方案。

PostgreSQL 默认使用火山模型(Volcano Model):每个算子(Scan、Join、Sort)实现一个 next() 接口,父算子调用子算子的 next() 获取下一行数据。这种模型非常灵活,但函数调用开销大。PG 14+ 开始引入向量化执行,批量处理多行数据以提升性能。


四、Doris:FE/BE 分离 + MPP 并行执行

Doris 是面向分析的 MPP(大规模并行处理)数据库,其架构设计和查询流程体现了分布式 OLAP 的核心思想。

执行流程

Client  │  ▼┌─────────────────────────────────────────┐│              FE (Frontend)               ││                                          ││  ┌─────────┐   ┌─────────┐   ┌────────┐ ││  │  Parser │──▶│ Analyzer│──▶│Planner │ ││  │         │   │         │   │  (CBO) │ ││  └─────────┘   └─────────┘   └───┬────┘ ││                                  │       ││  生成单节点计划 → 生成分布式计划      │       ││  (哪些 BE 执行哪些 Fragment)        │       │└─────────────────────────────────────────┘                    │                    ▼        ┌──────────┼──────────┐        ▼          ▼          ▼      BE 1       BE 2       BE 3┌─────────────┐ ┌─────────────┐ ┌─────────────┐│   Fragment   │ │   Fragment   │ │   Fragment   ││   Executor   │ │   Executor   │ │   Executor   ││              │ │              │ │              ││ ┌─────────┐  │ │ ┌─────────┐  │ │ ┌─────────┐  ││ │ Scan    │  │ │ │ Scan    │  │ │ │ Scan    │  ││ │ (列存)  │  │ │ │ (列存)  │  │ │ │ (列存)  │  ││ └────┬────┘  │ │ └────┬────┘  │ │ └────┬────┘  ││      │       │ │      │       │ │      │       ││      ▼       │ │      ▼       │ │      ▼       ││ ┌─────────┐  │ │ ┌─────────┐  │ │ ┌─────────┐  ││ │Exchange │◀─┼─┼▶│Exchange │◀─┼─┼▶│Exchange │  │  ← 数据 Shuffle│ │ (网络)  │  │ │ │ (网络)  │  │ │ │ (网络)  │  ││ └────┬────┘  │ │ └────┬────┘  │ │ └────┬────┘  ││      │       │ │      │       │ │      │       ││      ▼       │ │      ▼       │ │      ▼       ││ ┌─────────┐  │ │ ┌─────────┐  │ │ ┌─────────┐  ││ │  Join   │  │ │ │ Aggregate│  │ │ │  Sort   │  ││ │  Hash   │  │ │ │  (本地)  │  │ │ │         │  ││ └────┬────┘  │ │ └────┬────┘  │ │ └────┬────┘  ││      │       │ │      │       │ │      │       ││      └───────┼─┼──────┘       │ │      │       ││              │ │              │ │      │       │└──────────────┼─┼──────────────┘ │      │       │               │ │                │      │       │               ▼ ▼                ▼      ▼       │          ┌─────────────────────────────┐        │          │      FE 合并最终结果          │◀───────┘          └─────────────────────────────┘

涉及的核心模块

模块职责
FE (Frontend)SQL 解析、语义分析、查询优化、分布式计划生成
BE (Backend)执行 Fragment(查询计划片段),数据扫描和计算
Fragment分布式执行的最小单位,一个 BE 上的查询子计划
Exchange NodeBE 之间的数据交换(Shuffle/Broadcast)
StorageTablet(数据分片)、Rowset、Segment、Page Cache

特色说明

Doris 的核心设计是 FE/BE 分离:

  • FE 是无状态的查询协调者,负责 SQL 解析和计划生成。它会将查询计划切分为多个 Fragment,每个 Fragment 对应一个 BE 上的执行单元。
  • BE 是实际干活的数据节点,负责从本地存储读取数据并执行计算。

MPP 的精髓在于 Exchange Node:当数据需要重新分布时(如 Hash Join 前需要将相同 Key 的数据集中到同一个 BE),BE 之间通过网络直接交换数据,不需要经过 FE。这使得 Doris 可以线性扩展到数百个节点。

Doris 同时采用了向量化执行引擎,算子内部一次处理一批数据(而非一行),配合列存格式,分析性能远超传统行存数据库。


五、ClickHouse:Pipeline 模型 + Part 不可变

ClickHouse 以其极致的分析性能著称,其查询执行流程和存储设计都有鲜明的特色。

执行流程

Client  │  ▼┌─────────┐│  Parser │  ← SQL 解析└────┬────┘     │     ▼┌───────────┐│Interpreter│  ← AST 语义分析、查询重写└─────┬─────┘      │      ▼┌───────────┐│  Planner  │  ← 生成执行计划└─────┬─────┘      │      ▼┌─────────────────────────────────────────┐│      Pipeline Executor                  ││      (多线程并行 + 向量化)               ││                                         ││  ┌─────────────────────────────────┐    ││  │  ReadFromMergeTree              │    ││  │  - 主键稀疏索引定位 Parts        │    ││  │  - 跳过多余 Granules             │    ││  └─────────────┬───────────────────┘    ││                │                        ││                ▼                        ││  ┌─────────────────────────────────┐    ││  │  Expression / Filter            │    ││  │  (向量化过滤,一次 65536 行)      │    ││  └─────────────┬───────────────────┘    ││                │                        ││                ▼                        ││  ┌─────────────────────────────────┐    ││  │  Join (如果有)                   │    ││  │  - Hash Join / Merge Join       │    ││  └─────────────┬───────────────────┘    ││                │                        ││                ▼                        ││  ┌─────────────────────────────────┐    ││  │  AggregatingTransform           │    ││  │  (两阶段聚合:本地 + 全局)        │    ││  └─────────────┬───────────────────┘    ││                │                        ││                ▼                        ││  ┌─────────────────────────────────┐    ││  │  MergeTree 存储层               │    ││  │  - Parts (不可变数据块)         │    ││  │  - Primary Index (稀疏索引)     │    ││  │  - Skip Indexes (跳数索引)      │    ││  │  - Column files (列存文件)      │    ││  └─────────────────────────────────┘    │└─────────────────────────────────────────┘      │      ▼   Result

涉及的核心模块

模块职责
Interpreter将 AST 解释为可执行的操作序列
Planner生成逻辑和物理执行计划
Pipeline Executor多线程并行执行,采用 Push 模型
MergeTree存储引擎,数据按 Part 不可变存储,后台 Merge
Primary Index稀疏索引(每 8192 行一条),极小,常驻内存
Skip Index跳数索引(如 min/max、布隆过滤器),进一步减少数据读取

特色说明

ClickHouse 的 Pipeline Executor 采用 Push 模型(而非 PostgreSQL 的 Pull 模型):数据从存储层被推上来,经过各级算子处理。每个算子可以有多线程实例并行执行。

MergeTree 是 ClickHouse 最核心的存储设计:

  • 数据写入时生成一个不可变的 Part
  • 后台线程异步合并(Merge)小 Part 为大 Part
  • 查询时通过稀疏索引跳过不相关的 Granule(8192 行一组)
  • 这种设计让写入极快(顺序追加),查询时只读需要的 Granule

ClickHouse 的向量化执行默认一次处理 65536 行,配合 SIMD 指令,单核分析性能极高。


六、Milvus:多角色分布式 + ANN 索引

Milvus 是专用的向量数据库,其架构设计完全围绕”高维向量相似性搜索”这一核心场景展开。

执行流程

Client  │  ▼┌─────────────────────────────────────────┐│              Proxy                       ││  - 接收请求                              ││  - 鉴权、路由                            ││  - 结果合并                              │└─────────────┬───────────────────────────┘              │              ▼┌─────────────────────────────────────────┐│         Query Coordinator                ││  - 管理 Query Node 分布                  ││  - 负载均衡                              │└─────────────┬───────────────────────────┘              │      ┌───────┴───────┐      ▼               ▼  Query Node 1    Query Node 2 ...┌─────────────────────────────────────────┐│  ┌─────────────┐    ┌─────────────┐    ││  │   Segment   │    │   Index     │    ││  │  (原始向量)  │    │ (HNSW/IVF)  │    ││  │  内存/磁盘   │    │   内存中     │    ││  └─────────────┘    └──────┬──────┘    ││                             │           ││  ┌──────────────────────────┘           ││  ▼                                      ││  ANN 搜索算法                            ││  - HNSW: 图遍历贪心搜索                   ││  - IVF: 先找最近聚类中心,再搜该桶        ││  - 计算距离 (L2/IP/Cosine)              ││  - 标量过滤 (如有 metadata 条件)         ││  └────────────────────────────────────  │└─────────────────────────────────────────┘              │              ▼┌─────────────────────────────────────────┐│         结果合并 + Rerank                ││  (多个 Query Node 的局部 Top-K 合并)      │└─────────────────────────────────────────┘      │      ▼   Result【后台独立模块】┌─────────────┐  ┌─────────────┐  ┌─────────────┐│ Index Node  │  │  Data Node  │  │  Meta Store ││ (建索引)     │  │ (数据写入)   │  │   (etcd)    │└─────────────┘  └─────────────┘  └─────────────┘

涉及的核心模块

模块职责
Proxy接收客户端请求,鉴权,路由到 Query Coordinator
Query Coordinator管理 Query Node 的负载和分布
Query Node执行向量搜索,加载索引到内存,计算相似度
Index Node异步构建向量索引(HNSW、IVF 等)
Data Node处理数据写入,将数据持久化到对象存储
Meta Store (etcd)存储元数据(Collection、Partition、Index 信息)
Object StorageMinIO/S3,存储原始向量数据和索引文件
Message QueuePulsar/Kafka,数据写入的缓冲队列

特色说明

Milvus 的查询流程和数据库完全不同:

  • 没有 SQL 解析和查询优化器,查询参数直接描述搜索行为(向量 + 相似度度量 + Top-K + 标量过滤条件)
  • ANN 搜索是核心:根据向量索引(HNSW 图或 IVF 聚类)快速定位最近邻,不需要全量扫描
  • 标量过滤 + 向量搜索的混合查询是难点:先通过标量条件缩小候选集,再在候选集上做向量搜索

Milvus 的多角色架构让其可以独立扩展:Query Node 不够就加查询节点,索引构建慢就加 Index Node。


七、NebulaGraph:图遍历 + LSM-Tree 存储

NebulaGraph 是分布式图数据库,其查询执行流程围绕”从起点出发,沿着边遍历”这一核心操作展开。

执行流程

Client  │  ▼┌─────────────────────────────────────────┐│           Graph Service (GraphD)         ││                                          ││  ┌─────────┐   ┌─────────┐   ┌────────┐ ││  │  Parser │──▶│  Planner│──▶│Optimizer│ ││  │ (nGQL)  │   │         │   │  (RBO) │ ││  └─────────┘   └─────────┘   └───┬────┘ ││                                  │       ││  生成执行计划(获取起点 → 遍历边 → 过滤)  │└─────────────────────────────────────────┘                    │                    ▼        ┌──────────┼──────────┐        ▼          ▼          ▼   StorageD 1  StorageD 2  StorageD 3┌─────────────────────────────────────────┐│  每个 StorageD 内部:                     ││                                         ││  1. 找起点 Vertex                        ││     - 如果按 VID 查:直接定位              ││     - 如果按属性查:走属性索引 → 查 VID    ││                                         ││  2. 遍历 Edge                            ││     - 根据 VID 做 prefix scan            ││     - 读取出边/入边列表                   ││                                         ││  3. 多跳扩展                              ││     - 重复步骤 1-2                       ││                                         ││  4. 属性过滤                              ││     - 读取 Vertex/Edge 属性               ││     - WHERE 条件过滤                      ││                                         ││  底层:RocksDB (LSM-Tree)                ││  - Tag 和 Edge 数据以 KV 形式存储         ││  - Key 格式:Type + VID + EdgeType + Rank │└─────────────────────────────────────────┘                    │                    ▼         GraphD 合并结果                    │                    ▼                 Result【Meta Service (MetaD)】- Schema 信息(Tag、EdgeType 定义)- 分区分布信息(哪个 VID 在哪个 StorageD)- 索引信息- 基于 Raft 保证一致性

涉及的核心模块

模块职责
GraphD查询解析、计划生成、结果合并
StorageD图数据存储,执行顶点/边的读取和遍历
MetaD元数据管理(Schema、分区、索引),基于 Raft 保证一致性
RocksDBStorageD 的底层存储引擎(LSM-Tree)
Raft多副本一致性协议

特色说明

NebulaGraph 的查询执行完全围绕图遍历设计:

  • VID 直接路由:每个 Vertex 有全局唯一的 VID(Vertex ID),通过 part_id = vid % num_parts 直接路由到对应的 StorageD,不需要分布式查询协调
  • Prefix Scan 遍历边:给定一个 VID,StorageD 在 RocksDB 中做前缀扫描,一次性读出该顶点的所有出边/入边
  • 多跳扩展:一跳查询返回的结果作为下一跳的起点,重复上述过程

和 Neo4j 不同的是,NebulaGraph 的底层存储是 RocksDB(LSM-Tree) 而非自定义文件格式。图数据被编码为 KV 对存储:

  • Vertex Key:Type + VID + TagType -> Value: 属性
  • Edge Key:Type + SrcVID + EdgeType + Rank + DstVID -> Value: 属性

这种设计的优势是可以利用成熟的 LSM-Tree 存储引擎,代价是图的遍历需要多次 KV 查询(而非 Neo4j 的直接指针跳转)。


横向对比总结

系统架构模式执行模型查询核心存储模型
MongoDB单体/副本集/分片管道执行B+Tree 索引跳转行存(BSON 文档)
ES分片 + 副本 + 协调节点两阶段查询倒排索引 + 评分行存 + 倒排索引 + Doc Values
PostgreSQL单节点/流复制火山模型(逐行)成本优化器 + B+Tree行存(Heap)
DorisFE + BE 分离、MPPPipeline + 向量化分布式计划 + Shuffle列存
ClickHouse单节点/分布式表Pipeline + 向量化稀疏索引 + Part 裁剪列存(MergeTree)
MilvusProxy + Node 多角色ANN 图遍历/聚类HNSW / IVF 索引向量 + 标量分离
NebulaGraphGraph + Storage + Meta图遍历VID 路由 + Prefix ScanLSM-Tree(RocksDB)

选型启示

从查询执行流程的角度,不同类型数据库的选型逻辑可以总结为:

如果你的查询模式是”已知 ID,查详情”:选择行存数据库(MongoDB、PostgreSQL),它们通过索引一次定位、一次读取。

如果你的查询模式是”全文搜索,返回最相关的 N 个”:选择 Elasticsearch,倒排索引 + 评分机制是为此而生的。

如果你的查询模式是”扫描大量数据,做聚合分析”:选择 OLAP 数据库(Doris、ClickHouse),列存 + 向量化 + MPP 并行是核心优势。

如果你的查询模式是”找一个向量的最近邻”:选择向量数据库(Milvus),ANN 索引让十亿级向量的相似性搜索在毫秒级完成。

如果你的查询模式是”从一个点出发,沿关系找多跳邻居”:选择图数据库(NebulaGraph),图遍历的执行路径比关系数据库的多表 JOIN 高效数个数量级。

理解每个数据库的查询执行流程,就是理解它为什么适合(或不适合)你的场景。没有最好的数据库,只有最适合查询模式的数据库。

© 本文著作权归作者所有,转载请注明出处。

未知标题
00:00
00:00
播放列表