时间点 API编辑

默认情况下,搜索请求针对目标索引的最新可见数据执行,这称为时间点。Elasticsearch PIT(时间点)是对数据状态的轻量级视图,该视图反映了数据在启动时存在的状态。在某些情况下,最好使用相同的时间点执行多个搜索请求。例如,如果 刷新发生在 search_after 请求之间,那么这些请求的结果可能不一致,因为搜索之间发生的更改仅对较新的时间点可见。

先决条件编辑

  • 如果启用了 Elasticsearch 安全功能,则您必须对目标数据流、索引或别名具有 read 索引权限

    要搜索 时间点 (PIT) 的别名,您必须对别名的 数据流或索引具有 read 索引权限。

请求正文编辑

index_filter
(可选,查询对象 允许过滤索引,如果提供的查询在每个分片上重写为 match_none

示例编辑

必须在搜索请求中使用时间点之前显式打开时间点。keep_alive 参数告诉 Elasticsearch 应该将时间点保持活动多长时间,例如 ?keep_alive=5m

response = client.open_point_in_time(
  index: 'my-index-000001',
  keep_alive: '1m'
)
puts response
POST /my-index-000001/_pit?keep_alive=1m

上述请求的结果包含一个 id,该 id 应传递到搜索请求的 pit 参数的 id 中。

POST /_search  
{
    "size": 100,  
    "query": {
        "match" : {
            "title" : "elasticsearch"
        }
    },
    "pit": {
	    "id":  "46ToAwMDaWR5BXV1aWQyKwZub2RlXzMAAAAAAAAAACoBYwADaWR4BXV1aWQxAgZub2RlXzEAAAAAAAAAAAEBYQADaWR5BXV1aWQyKgZub2RlXzIAAAAAAAAAAAwBYgACBXV1aWQyAAAFdXVpZDEAAQltYXRjaF9hbGw_gAAAAA==", 
	    "keep_alive": "1m"  
    }
}

具有 pit 参数的搜索请求不得指定 indexroutingpreference,因为这些参数是从时间点复制的。

与常规搜索一样,您可以 使用 fromsize 对搜索结果进行分页,最多可达前 10,000 个匹配项。如果您想检索更多匹配项,请将 PIT 与 search_after 一起使用。

id 参数告诉 Elasticsearch 使用此时间点的上下文执行请求。

keep_alive 参数告诉 Elasticsearch 应该将时间点的生存时间延长多长时间。

打开时间点请求和每个后续搜索请求都可能返回不同的 id;因此,始终对下一个搜索请求使用最近收到的 id

保持时间点活动编辑

传递给打开时间点请求和搜索请求的 keep_alive 参数会延长相应时间点的生存时间。该值(例如 1m,请参阅 时间单位)不需要足够长以处理所有数据,它只需要足够长以进行下一个请求。

通常,后台合并过程通过将较小的段合并在一起以创建新的、更大的段来优化索引。一旦不再需要较小的段,它们就会被删除。但是,打开的时间点会阻止旧段被删除,因为它们仍在使用中。

保持旧段活动意味着需要更多磁盘空间和文件句柄。确保您已将节点配置为具有足够的可用文件句柄。请参阅 文件描述符

此外,如果段包含已删除或更新的文档,则时间点必须跟踪段中的每个文档在初始搜索请求时是否处于活动状态。如果您在索引上有很多打开的时间点,并且该索引会不断删除或更新,请确保您的节点具有足够的堆空间。请注意,时间点不会阻止其关联的索引被删除。

您可以使用 节点统计信息 API 检查有多少时间点(即搜索上下文)处于打开状态。

$params = [
    'metric' => 'indices',
    'index_metric' => 'search',
];
$response = $client->nodes()->stats($params);
response = client.nodes.stats(
  metric: 'indices',
  index_metric: 'search'
)
puts response
res, err := es.Nodes.Stats(
	es.Nodes.Stats.WithMetric([]string{"indices"}...),
	es.Nodes.Stats.WithIndexMetric([]string{"search"}...),
)
fmt.Println(res, err)
const response = await client.nodes.stats({
  metric: 'indices',
  index_metric: 'search'
})
console.log(response)
GET /_nodes/stats/indices/search

关闭时间点 API编辑

当时间点的 keep_alive 已过期时,时间点会自动关闭。但是,正如 上一节 中所述,保持时间点会产生成本。时间点应在不再用于搜索请求时立即关闭。

response = client.close_point_in_time(
  body: {
    id: '46ToAwMDaWR5BXV1aWQyKwZub2RlXzMAAAAAAAAAACoBYwADaWR4BXV1aWQxAgZub2RlXzEAAAAAAAAAAAEBYQADaWR5BXV1aWQyKgZub2RlXzIAAAAAAAAAAAwBYgACBXV1aWQyAAAFdXVpZDEAAQltYXRjaF9hbGw_gAAAAA=='
  }
)
puts response
DELETE /_pit
{
    "id" : "46ToAwMDaWR5BXV1aWQyKwZub2RlXzMAAAAAAAAAACoBYwADaWR4BXV1aWQxAgZub2RlXzEAAAAAAAAAAAEBYQADaWR5BXV1aWQyKgZub2RlXzIAAAAAAAAAAAwBYgACBXV1aWQyAAAFdXVpZDEAAQltYXRjaF9hbGw_gAAAAA=="
}

该 API 返回以下响应

{
   "succeeded": true, 
   "num_freed": 3     
}

如果为 true,则与时间点 ID 关联的所有搜索上下文都已成功关闭

已成功关闭的搜索上下文数量

搜索切片编辑

当对大量文档进行分页时,将搜索拆分为多个切片以独立使用它们可能会有所帮助。

GET /_search
{
  "slice": {
    "id": 0,                      
    "max": 2                      
  },
  "query": {
    "match": {
      "message": "foo"
    }
  },
  "pit": {
    "id": "46ToAwMDaWR5BXV1aWQyKwZub2RlXzMAAAAAAAAAACoBYwADaWR4BXV1aWQxAgZub2RlXzEAAAAAAAAAAAEBYQADaWR5BXV1aWQyKgZub2RlXzIAAAAAAAAAAAwBYgACBXV1aWQyAAAFdXVpZDEAAQltYXRjaF9hbGw_gAAAAA=="
  }
}

GET /_search
{
  "slice": {
    "id": 1,
    "max": 2
  },
  "pit": {
    "id": "46ToAwMDaWR5BXV1aWQyKwZub2RlXzMAAAAAAAAAACoBYwADaWR4BXV1aWQxAgZub2RlXzEAAAAAAAAAAAEBYQADaWR5BXV1aWQyKgZub2RlXzIAAAAAAAAAAAwBYgACBXV1aWQyAAAFdXVpZDEAAQltYXRjaF9hbGw_gAAAAA=="
  },
  "query": {
    "match": {
      "message": "foo"
    }
  }
}

切片的 ID

切片的最大数量

第一个请求的结果返回属于第一个切片(ID:0)的文档,第二个请求的结果返回第二个切片中的文档。由于切片的最大数量设置为 2,因此两个请求结果的并集等效于没有切片的 时间点搜索的结果。默认情况下,拆分首先在分片上进行,然后在每个分片上本地进行。本地拆分根据 Lucene 文档 ID 将分片划分为连续范围。

例如,如果分片数等于 2 并且用户请求了 4 个切片,那么切片 0 和 2 会分配给第一个分片,切片 1 和 3 会分配给第二个分片。

所有切片应使用相同的时间点 ID。如果使用不同的 PIT ID,则切片可能会重叠并遗漏文档。这是因为拆分标准基于 Lucene 文档 ID,而 Lucene 文档 ID 在索引更改时不稳定。