在本地 vespa 中查询非常慢

Queries are very slow in local vespa

我在正确执行 vespa 查询时遇到困难。 我想在它们之间查询 2 个不同的索引字段,我想等效于弹性匹配查询。

我有很多软超时,所以我增加了超时以获得真实结果并检查花费了多少时间。

这是我发送的查询:

{
    "name": "some name",
    "timeout": 10,
    "traceLevel": 4,
    "ranking": {
        "profile": "addition_score"
    },
    "hits": 2,
    "address__street_address": "some street address",
    "yql": "select * from sources * where   (([{\"grammar\":\"any\",\"defaultIndex\":\"address__street_address\"}] userInput(@address__street_address))  or ([{\"grammar\":\"any\",\"defaultIndex\":\"name\"}] userInput(@name))) and address__address_region contains \"us-ca\"   ;"}
}

以及我第二次添加的trace数据:

{
                                                "timestamp_ms": 4978.7203,
                                                "tag": "match_threads",
                                                "threads": [
                                                    {
                                                        "traces": [
                                                            {
                                                                "timestamp_ms": 12.1874,
                                                                "event": "Start MatchThread::run"
                                                            },
                                                            {
                                                                "timestamp_ms": 12.2481,
                                                                "event": "Start match and first phase rank"
                                                            },
                                                            {
                                                                "timestamp_ms": 4976.272,
                                                                "event": "Start second phase rerank"
                                                            },
                                                            {
                                                                "timestamp_ms": 4977.3873,
                                                                "event": "Create result set"
                                                            },
                                                            {
                                                                "timestamp_ms": 4978.6731,
                                                                "event": "Start thread merge"
                                                            },
                                                            {
                                                                "timestamp_ms": 4978.6816,
                                                                "event": "MatchThread::run Done"
                                                            }
                                                        ]
                                                    }
                                                ]
                                            }
                                        ],
                                        "distribution-key": 0,
                                        "duration_ms": 4978.8584
                                    }
                                ]
                            }

根据我的理解,这意味着抓取和第一阶段花费了 5 秒,这对我来说似乎太长了。

i 运行 我本地机器上的 vespa docker 有 8 GB 的内存和大约 4000 万份文件。 架构如下:

    document organization {
        field name type string {
            indexing: index | summary
            weight : 100
        }
        field url type string {
            indexing: index
            weight : 10
        }
        field naics type string {
            indexing: attribute
            weight : 10
        }
        field number_of_employees type int {
            indexing: attribute
            weight : 1
        }
        field is_hq type bool {
            indexing: attribute
            weight : 1
        }
        field address__address_country type string {
            indexing: attribute | summary
            weight : 10
        }
        field address__address_region type string {
            indexing: attribute | summary
            weight : 20
        }
        field address__address_locality type string {
            indexing: index | summary
            weight : 50
        }
        field address__postal_code type string {
            indexing: attribute
            weight : 70
        }
        field address__street_address type string {
            indexing: index | summary
            weight : 100
        }
        field duns type string {
            indexing: attribute | summary
            weight : 1000
        }
        field density type int{
            indexing: attribute
            weight : 1
        }
    }

    fieldset default {
        fields: name
    }
    rank-profile native {
        first-phase {
            expression: nativeRank(name,address__street_address)
        }
        second-phase{
           expression: fieldMatch(name) * fieldMatch(address__street_address)
        }
        summary-features {
             nativeRank(name)
             nativeRank(address__street_address)
             fieldMatch(name)
             fieldMatch(address__street_address)
        }
    }
     rank-profile addition_score {
        first-phase {
           expression: nativeRank(address__street_address,name)*(attribute(density)/11)
        }
        second-phase {
            expression: (fieldMatch(name)*100 + fieldMatch(address__street_address)*100+attributeMatch(address__address_country)*10+fieldMatch(address__address_locality)*20+attributeMatch(address__postal_code)*50 +attribute(density))/(290)
        }
        summary-features {
             attributeMatch(duns)
             fieldMatch(name)
             fieldMatch(address__address_locality)
             fieldMatch(address__street_address)
             attributeMatch(address__address_country)
             attributeMatch(address__postal_code)
        }
    }
}

我做错了什么?

请参阅此处关于索引与属性的部分以及快速搜索文档 https://docs.vespa.ai/en/performance/feature-tuning.html

默认情况下,具有属性定义的字段无法快速搜索,这可能是这里的问题。添加快速搜索属性 属性 将构建 B 树结构以加快搜索速度。

 field address__postal_code type string {
            indexing: attribute
            attribute:fast-search
            weight : 70
}

背景 Vespa 具有索引和 attribute concept for indexing,如下图所示

schema doc {
  document doc {
   field license type string {
      indexing: summary | attribute
   }
   field title type string {
      indexing: summary | index
   }
   }
   rank-profile my-ranking {
     first-phase { expression: nativeRank(title) }
   }
} 

由于默认 match setting, the type of processing you expect for text matching,标题字段将被标记化和词干化。默认情况下,索引字段使用文本匹配(根据 unit/token/atom)进行匹配。由于倒排索引结构,索引字段可能会在 sub-linear 时间内被搜索。

license字段没有索引定义,有属性。属性字段将始终在内存中。默认匹配模式与索引不同,适用于精确匹配、无标记化和无词干化。同样默认情况下,属性字段没有反向 index-like 结构。可以通过添加 attribute:fast-search 来添加它,但代价是会占用更多内存。这个和较慢的整体索引是 Vespa 默认不使用 fast-search.

的主要原因

现在,关于这对 search performance 意味着什么。假设我们使用简化模式将 10M 文档存储在 Vespa 集群中:

/search/?query=license:unk&yql=select id,license from doc where userQuery();&ranking=my-ranking

上述请求搜索license属性字段,但由于没有inverted-like索引结构,所以搜索是线性的10M文档。也就是说,搜索过程会遍历所有10M的文档,从内存中读取license字段的值,并与输入的query term进行比较,如果term匹配该值,则暴露给ranking profile进行排名。这不是特别有效。如果我们将属性定义更改为还包含 fast-search,那么 Vespa 将构建 inverted-index 类数据结构:

 field license type string {
      indexing: summary | attribute
      attribute:fast-search
 }

如果我们部署它并遵循 changes which require restart,我们的查询将使用 inverted-index 结构。然后,搜索过程从仅线性遍历变为在字典中查找术语并遍历发布列表。如果术语“unk”出现在少于 1000 万的文档中,则搜索变为 sub-linear 并且少于 1000 万的文档会被排名。

上面是一个简化的例子,如果我们有一个更复杂的查询来搜索多个字段呢?

/search/?query=license:unk title:foo&yql=select id,license from doc where userQuery();&ranking=my-ranking

在上面的示例中,我们在 license:unk 之间使用 AND 进行搜索,并且标题需要包含术语 foo(标记化文本匹配)。在这种情况下(以及其他情况),搜索过程会构建一个查询执行计划,以了解如何有效地将查询与索引相匹配。在许可证字段没有 fast-search 的情况下,查询计划将假定该术语出现在所有文档中(最坏情况)。但是,由于我们包含一个定义了索引的术语,它可以知道命中数的上限,并且整体搜索变为 sub-linear。但是,如果我们使用 OR 将 license:unk 与 title:foo 组合,搜索将变成线性的,因为我们要求具有 license:unk 或标记 foo 出现在标题中(逻辑析取) .

如何调试单个查询的匹配和排名性能?

运行 具有预期排名概况的代表性查询,并查看 totalCount 与搜索的文档数。结果模板中提供了此信息(请参阅文档搜索数量的覆盖范围)。查询的总体成本高度依赖于 totalCount,更高的 totalCount 意味着暴露于第一阶段排名的文档数量更多。 如果查询速度很慢但检索到的文档相对较少,则清楚地表明匹配已触及线性扫描路径。然而,排名使这变得复杂,因为第一阶段排名配置文件的复杂性也会影响性能。 Vespa 有一个 built-in ‘unranked’ ranking profile,可用于量化匹配与排名性能。如果使用 unranked rank-profile 可以找到搜索性能的下界,没有任何第一阶段排名,这意味着我们可以单独调试匹配性能。

查询跟踪:

可以通过在搜索请求中添加&tracelevel=6&trace.timestamps=1来检查查询的内容节点查询计划。来自查询中涉及的每个内容节点的查询蓝图随后包含在跟踪的响应中。在蓝图查询树跟踪中将有一个 docid_limit,它是索引文档的数量(从 1 开始计数),因此对于我们的 10M 文档的情况,它将是 10M +1。如果查询树顶根的 estHits 等于 docid_limit 那么整体复杂度是线性的。

查询蓝图示例

在此示例中,查询树的顶层根估计命中总数将等于 docid_limit(已索引的文档数)。这表示线性匹配。

在此示例中,查询树的顶层根估计的命中数要低得多(由于查询树中存在 must-have 项,它具有索引或 attribute:fast -搜索。然后 限制 查询以匹配更少的文档ts 比索引和匹配和排名变成 sub-linear.