# 基于代价的优化器

当 Manticore 执行全扫描查询时，它可以使用普通扫描检查每个文档是否符合过滤条件，或者采用额外的数据和/或算法来加快查询执行速度。Manticore 使用基于代价的优化器（CBO），也称为“查询优化器”，来确定采用哪种方式。

CBO 还可以提升全文查询的性能。详见下文。

如果 CBO 判断某些措施能够提升性能，它可能决定将一个或多个查询过滤器替换为以下实体之一：

1. **docid 索引** 使用一种仅包含 docid 的特殊二级索引，存储在带有 `.spt` 扩展名的文件中。除了改进对数字文档 ID 的过滤之外，docid 索引还用于加速文档 ID 到 row ID 的查找，并在守护进程启动期间加快大规模 killlist 的应用。
2. **列式扫描** 依赖于列式存储，只能用于列式属性。它扫描每个值并对其应用过滤器，但进行了高度优化，通常比默认方法更快。
3. **二级索引** 默认为所有属性（除 JSON 外）生成。它们使用 [PGM 索引](https://pgm.di.unipi.it/) 以及 Manticore 内置的倒排索引来检索对应某个值或值范围的行 ID 列表。二级索引存储在扩展名为 `.spidx` 和 `.spjidx` 的文件中。
有关如何为 JSON 属性生成二级索引的信息，请参见 [json_secondary_indexes](../Creating_a_table/Local_tables/Plain_and_real-time_table_settings.md#json_secondary_indexes)。

优化器利用各种属性统计信息估算每条执行路径的代价，包括：

1. 关于属性内数据分布的信息（直方图，存储在 `.sphi` 文件中）。直方图在数据建立索引时自动生成，是 CBO 的主要信息来源。
2. PGM（二级索引）信息，有助于估算需要读取的文档列表数量。这有助于评估文档列表合并性能及选择合适的合并算法（优先队列合并或位图合并）。
3. 列式编码统计数据，用于估算列式数据解压性能。
4. 列式最小-最大树。CBO 使用直方图估算应用过滤器后剩余的文档数，同时需要确定过滤器处理过的文档数。对于列式属性，部分评估最小-最大树用于此目的。
5. 全文字典。CBO 利用词项统计信息估算全文树评估代价。

优化器会计算查询中每个过滤器的执行成本。由于某些过滤器可以用多种不同实体替代（例如，对于数字文档 ID，Manticore 可以使用普通扫描、docid 索引查找、列式扫描（如果文档 ID 是列式的）以及二级索引），优化器会评估所有可用的组合。不过，组合数量的上限是 1024。

为估算查询执行代价，优化器计算执行查询时所执行的最重要操作的预估代价。它采用预设常数代表每个操作的代价。

优化器对比各执行路径的代价，选择代价最低的路径以执行查询。

当处理带有属性过滤器的全文查询时，查询优化器在两条可能的执行路径间做出决定。一种是执行全文查询，获取匹配结果并应用过滤器；另一种是用上述一个或多个实体替换过滤器，从中获取行 ID 并注入全文匹配树。这样，全文搜索结果会与全扫描结果取交集。查询优化器估算全文树评估成本和计算过滤器结果的最佳路径，利用这些信息选择执行路径。

另一个考虑因素是多线程查询执行（当启用 `pseudo_sharding` 时）。CBO 意识到某些查询可并发执行，并将其纳入考量。CBO 优先考虑更短的查询执行时间（即延迟）胜过吞吐量。例如，如果使用列式扫描的查询能在多线程（占用多个 CPU 核心）中执行，且比单线程使用二级索引的查询更快，则优先多线程执行。

使用二级索引和 docid 索引的查询始终单线程执行，因为基准测试表明它们多线程执行几乎无益。

目前，优化器仅考虑 CPU 代价，不考虑内存或磁盘使用。

<!-- proofread -->

