分布式搜索

一个CRUD操作只处理一个单独的文档。文档的唯一性由_index, _type和routing-value(通常默认是该文档的_id)的组合来确定。这意味着我们可以准确知道集群中的哪个分片持有这个文档。

由于不知道哪个文档会匹配查询(文档可能存放在集群中的任意分片上),所以搜索需要一个更复杂的模型。一个搜索不得不通过查询每一个我们感兴趣的索引的分片副本,来看是否含有任何匹配的文档。

但是,找到所有匹配的文档只完成了这件事的一半。在搜索(search)API返回一页结果前,来自多个分片的结果必须被组合放到一个有序列表中。因此,搜索的执行过程分两个阶段,称为查询然后取回(query then fetch)。

2.1 查询阶段

在初始化查询阶段(query phase),查询被索引中的每个分片副本(原本或副本)广播。每个分片在本地执行搜索并且建立了匹配document的优先队列(priority queue)。

一个优先队列(priority queue is)只是一个存有前n个(top-n)匹配document的有序列表。这个优先队列的大小由分页参数from和size决定。

这个查询的过程被描述在图分布式搜索查询阶段中。

查询阶段包含以下三步:

  • 1.客户端发送一个search(搜索)请求给Node 3,Node 3创建了一个长度为from+size的空优先级队列。
  • 2.Node 3 转发这个搜索请求到索引中每个分片的原本或副本。每个分片在本地执行这个查询并且结果将结果到一个大小为from+size的有序本地优先队列里去。
  • 3.每个分片返回document的ID和它优先队列里的所有document的排序值给协调节点Node 3。Node 3把这些值合并到自己的优先队列里产生全局排序结果。

当一个搜索请求被发送到一个节点Node,这个节点就变成了协调节点。这个节点的工作是向所有相关的分片广播搜索请求并且把它们的响应整合成一个全局的有序结果集。这个结果集会被返回给客户端。

整个过程类似于归并排序算法,先分组排序再归并到一起,对于这种分布式场景非常适用。

2.2 取回阶段

查询阶段辨别出那些满足搜索请求的document,但我们仍然需要取回那些document本身。这就是取回阶段的工作,如图分布式搜索的取回阶段所示。 取回阶段由以下步骤构成:

  • 1.协调节点辨别出哪个document需要取回,并且向相关分片发出GET请求。
  • 2.每个分片加载document并且根据需要丰富(enrich)它们,然后再将document返回协调节点。
  • 3.一旦所有的document都被取回,协调节点会将结果返回给客户端。

协调节点为每个持有相关document的分片建立多点get请求然后发送请求到处理查询阶段的分片副本。 分片加载document主体——_source field。如果需要,还会根据元数据丰富结果和高亮搜索片断。一旦协调节点收到所有结果,会将它们汇集到单一的回答响应里,这个响应将会返回给客户端。

2.3 扫描和滚屏

scan(扫描)搜索类型是和scroll(滚屏)API一起使用来从Elasticsearch里高效地取回巨大数量的结果而不需要付出深分页的代价。

scroll(滚屏)

一个滚屏搜索允许我们做一个初始阶段搜索并且持续批量从Elasticsearch里拉取结果直到没有结果剩下。这有点像传统数据库里的cursors(游标)。

滚屏搜索会及时制作快照。这个快照不会包含任何在初始阶段搜索请求后对index做的修改。它通过将旧的数据文件保存在手边,所以可以保护index的样子看起来像搜索开始时的样子。

scan(扫描)

深度分页代价最高的部分是对结果的全局排序,但如果禁用排序,就能以很低的代价获得全部返回结果。为达成这个目的,可以采用scan(扫描)搜索模式。扫描模式让Elasticsearch不排序,只要分片里还有结果可以返回,就返回一批结果。

为了使用scan-and-scroll(扫描和滚屏),需要执行一个搜索请求,将search_type 设置成scan,并且传递一个scroll参数来告诉Elasticsearch滚屏应该持续多长时间。

GET /old_index/_search?search_type=scan&scroll=1m (1)
{
    "query": { "match_all": {}},
    "size":  1000
}

results matching ""

    No results matching ""