跨集群搜索

编辑

跨集群搜索 允许您针对一个或多个远程集群运行单个搜索请求。例如,您可以使用跨集群搜索来过滤和分析存储在不同数据中心集群上的日志数据。

支持的 API

编辑

以下 API 支持跨集群搜索

  • 搜索
  • 异步搜索
  • 多重搜索
  • 搜索模板
  • 多重搜索模板
  • 字段能力
  • Painless 执行 API
  • 解析索引 API
  • [预览] 此功能为技术预览版,可能会在未来的版本中更改或删除。Elastic 将努力解决任何问题,但技术预览版中的功能不受官方 GA 功能的支持 SLA 的约束。 EQL 搜索
  • [预览] 此功能为技术预览版,可能会在未来的版本中更改或删除。Elastic 将努力解决任何问题,但技术预览版中的功能不受官方 GA 功能的支持 SLA 的约束。 SQL 搜索
  • [预览] 此功能为技术预览版,可能会在未来的版本中更改或删除。Elastic 将努力解决任何问题,但技术预览版中的功能不受官方 GA 功能的支持 SLA 的约束。 向量切片搜索
  • [预览] 此功能为技术预览版,可能会在未来的版本中更改或删除。Elastic 将努力解决任何问题,但技术预览版中的功能不受官方 GA 功能的支持 SLA 的约束。 ES|QL

先决条件

编辑
  • 如果您使用嗅探模式,则本地协调节点必须能够连接到远程集群上的种子节点和网关节点。

    我们建议使用能够充当协调节点的网关节点。种子节点可以是这些网关节点的子集。

  • 如果您使用代理模式,则本地协调节点必须能够连接到配置的proxy_address。此地址上的代理必须能够将连接路由到远程集群上的网关节点和协调节点。
  • 跨集群搜索在本地集群和远程集群上需要不同的安全权限。请参阅配置跨集群搜索的权限远程集群

跨集群搜索示例

编辑

远程集群设置

编辑

以下集群更新设置 API 请求添加了三个远程集群:cluster_onecluster_twocluster_three

resp = client.cluster.put_settings(
    persistent={
        "cluster": {
            "remote": {
                "cluster_one": {
                    "seeds": [
                        "35.238.149.1:9300"
                    ],
                    "skip_unavailable": True
                },
                "cluster_two": {
                    "seeds": [
                        "35.238.149.2:9300"
                    ],
                    "skip_unavailable": False
                },
                "cluster_three": {
                    "seeds": [
                        "35.238.149.3:9300"
                    ]
                }
            }
        }
    },
)
print(resp)
response = client.cluster.put_settings(
  body: {
    persistent: {
      cluster: {
        remote: {
          cluster_one: {
            seeds: [
              '35.238.149.1:9300'
            ],
            skip_unavailable: true
          },
          cluster_two: {
            seeds: [
              '35.238.149.2:9300'
            ],
            skip_unavailable: false
          },
          cluster_three: {
            seeds: [
              '35.238.149.3:9300'
            ]
          }
        }
      }
    }
  }
)
puts response
const response = await client.cluster.putSettings({
  persistent: {
    cluster: {
      remote: {
        cluster_one: {
          seeds: ["35.238.149.1:9300"],
          skip_unavailable: true,
        },
        cluster_two: {
          seeds: ["35.238.149.2:9300"],
          skip_unavailable: false,
        },
        cluster_three: {
          seeds: ["35.238.149.3:9300"],
        },
      },
    },
  },
});
console.log(response);
PUT _cluster/settings
{
  "persistent": {
    "cluster": {
      "remote": {
        "cluster_one": {
          "seeds": [
            "35.238.149.1:9300"
          ],
          "skip_unavailable": true
        },
        "cluster_two": {
          "seeds": [
            "35.238.149.2:9300"
          ],
          "skip_unavailable": false
        },
        "cluster_three": {  
          "seeds": [
            "35.238.149.3:9300"
          ]
        }
      }
    }
  }
}

由于未在 cluster_three 上设置 skip_unavailable,因此它使用默认值 false。有关详细信息,请参阅可选远程集群部分。

搜索单个远程集群

编辑

在搜索请求中,您将远程集群上的数据流和索引指定为 <remote_cluster_name>:<target>

以下搜索API 请求在单个远程集群 cluster_one 上搜索 my-index-000001 索引。

resp = client.search(
    index="cluster_one:my-index-000001",
    size=1,
    query={
        "match": {
            "user.id": "kimchy"
        }
    },
    source=[
        "user.id",
        "message",
        "http.response.status_code"
    ],
)
print(resp)
response = client.search(
  index: 'cluster_one:my-index-000001',
  body: {
    size: 1,
    query: {
      match: {
        'user.id' => 'kimchy'
      }
    },
    _source: [
      'user.id',
      'message',
      'http.response.status_code'
    ]
  }
)
puts response
const response = await client.search({
  index: "cluster_one:my-index-000001",
  size: 1,
  query: {
    match: {
      "user.id": "kimchy",
    },
  },
  _source: ["user.id", "message", "http.response.status_code"],
});
console.log(response);
GET /cluster_one:my-index-000001/_search
{
  "size": 1,
  "query": {
    "match": {
      "user.id": "kimchy"
    }
  },
  "_source": ["user.id", "message", "http.response.status_code"]
}

API 返回以下响应。请注意,当您搜索一个或多个远程集群时,会包含一个 _clusters 部分,以提供有关每个集群搜索的信息。

{
  "took": 150,
  "timed_out": false,
  "_shards": {
    "total": 12,
    "successful": 12,
    "failed": 0,
    "skipped": 0
  },
  "_clusters": {
    "total": 1,  
    "successful": 1,
    "skipped": 0,
    "running": 0,
    "partial": 0,
    "failed": 0,
    "details": {
      "cluster_one": {  
        "status": "successful",
        "indices": "my-index-000001", 
        "took": 148,  
        "timed_out": false,
        "_shards": {  
          "total": 12,
          "successful": 12,
          "skipped": 0,
          "failed": 0
        }
      }
    }
  },
  "hits": {
    "total" : {
        "value": 1,
        "relation": "eq"
    },
    "max_score": 1,
    "hits": [
      {
        "_index": "cluster_one:my-index-000001", 
        "_id": "0",
        "_score": 1,
        "_source": {
          "user": {
            "id": "kimchy"
          },
          "message": "GET /search HTTP/1.1 200 1070000",
          "http": {
            "response":
              {
                "status_code": 200
              }
          }
        }
      }
    ]
  }
}

此计数器部分显示所有可能的集群搜索状态以及当前处于该状态的集群搜索数量。集群可以是以下状态之一:运行中成功(所有分片上的搜索都成功)、部分成功(集群的至少一个分片上的搜索成功,并且至少一个分片失败)、跳过(在标记为 skip_unavailable=true 的集群上搜索失败)或 失败(在标记为 skip_unavailable=false 的集群上搜索失败)。

_clusters/details 部分显示有关每个集群搜索的元数据。

用户提供的索引表达式。如果您提供诸如 logs-* 之类的通配符,则此部分将显示带有通配符的值,而不是正在搜索的具体索引。

该集群上子搜索所用的时间(以毫秒为单位)。

该集群上子搜索的分片详细信息。

搜索响应正文在 _index 参数中包含远程集群的名称。

搜索多个远程集群

编辑

以下搜索API 请求在三个集群上搜索 my-index-000001 索引

  • 本地(“查询”)集群,有 10 个分片
  • 两个远程集群,cluster_one,有 12 个分片,cluster_two 有 6 个分片。
resp = client.search(
    index="my-index-000001,cluster_one:my-index-000001,cluster_two:my-index-000001",
    query={
        "match": {
            "user.id": "kimchy"
        }
    },
    source=[
        "user.id",
        "message",
        "http.response.status_code"
    ],
)
print(resp)
response = client.search(
  index: 'my-index-000001,cluster_one:my-index-000001,cluster_two:my-index-000001',
  body: {
    query: {
      match: {
        'user.id' => 'kimchy'
      }
    },
    _source: [
      'user.id',
      'message',
      'http.response.status_code'
    ]
  }
)
puts response
const response = await client.search({
  index:
    "my-index-000001,cluster_one:my-index-000001,cluster_two:my-index-000001",
  query: {
    match: {
      "user.id": "kimchy",
    },
  },
  _source: ["user.id", "message", "http.response.status_code"],
});
console.log(response);
GET /my-index-000001,cluster_one:my-index-000001,cluster_two:my-index-000001/_search
{
  "query": {
    "match": {
      "user.id": "kimchy"
    }
  },
  "_source": ["user.id", "message", "http.response.status_code"]
}

API 返回以下响应

{
  "took": 150,
  "timed_out": false,
  "num_reduce_phases": 4,
  "_shards": {
    "total": 28,
    "successful": 28,
    "failed": 0,
    "skipped": 0
  },
  "_clusters": {
    "total": 3,
    "successful": 3,
    "skipped": 0,
    "running": 0,
    "partial": 0,
    "failed": 0,
    "details": {
      "(local)": {            
        "status": "successful",
        "indices": "my-index-000001",
        "took": 21,
        "timed_out": false,
        "_shards": {
          "total": 10,
          "successful": 10,
          "skipped": 0,
          "failed": 0
        }
      },
      "cluster_one": {
        "status": "successful",
        "indices": "my-index-000001",
        "took": 48,
        "timed_out": false,
        "_shards": {
          "total": 12,
          "successful": 12,
          "skipped": 0,
          "failed": 0
        }
      },
      "cluster_two": {
        "status": "successful",
        "indices": "my-index-000001",
        "took": 141,
        "timed_out": false,
        "_shards": {
          "total" : 6,
          "successful" : 6,
          "skipped": 0,
          "failed": 0
        }
      }
    }
  },
  "hits": {
    "total" : {
        "value": 3,
        "relation": "eq"
    },
    "max_score": 1,
    "hits": [
      {
        "_index": "my-index-000001", 
        "_id": "0",
        "_score": 2,
        "_source": {
          "user": {
            "id": "kimchy"
          },
          "message": "GET /search HTTP/1.1 200 1070000",
          "http": {
            "response":
              {
                "status_code": 200
              }
          }
        }
      },
      {
        "_index": "cluster_one:my-index-000001", 
        "_id": "0",
        "_score": 1,
        "_source": {
          "user": {
            "id": "kimchy"
          },
          "message": "GET /search HTTP/1.1 200 1070000",
          "http": {
            "response":
              {
                "status_code": 200
              }
          }
        }
      },
      {
        "_index": "cluster_two:my-index-000001", 
        "_id": "0",
        "_score": 1,
        "_source": {
          "user": {
            "id": "kimchy"
          },
          "message": "GET /search HTTP/1.1 200 1070000",
          "http": {
            "response":
              {
                "status_code": 200
              }
          }
        }
      }
    ]
  }
}

本地(查询)集群被标识为“(local)”。

此文档的 _index 参数不包含集群名称。这意味着文档来自本地集群。

此文档来自 cluster_one

此文档来自 cluster_two

将异步搜索用于 ccs_minimize_roundtrips=true 的跨集群搜索

编辑

可以使用异步搜索 API 异步查询远程集群。跨集群搜索接受 ccs_minimize_roundtrips 参数。对于异步搜索,它默认为 false。(注意:对于同步搜索,它默认为 true。)请参阅选择是否在跨集群搜索中最小化往返次数的注意事项以了解有关此选项的更多信息。

以下请求使用 ccs_minimize_roundtrips=true 对三个集群(与上一个示例相同)执行 my-index-000001 索引的异步搜索。

resp = client.async_search.submit(
    index="my-index-000001,cluster_one:my-index-000001,cluster_two:my-index-000001",
    ccs_minimize_roundtrips=True,
    query={
        "match": {
            "user.id": "kimchy"
        }
    },
    source=[
        "user.id",
        "message",
        "http.response.status_code"
    ],
)
print(resp)
response = client.async_search.submit(
  index: 'my-index-000001,cluster_one:my-index-000001,cluster_two:my-index-000001',
  ccs_minimize_roundtrips: true,
  body: {
    query: {
      match: {
        'user.id' => 'kimchy'
      }
    },
    _source: [
      'user.id',
      'message',
      'http.response.status_code'
    ]
  }
)
puts response
const response = await client.asyncSearch.submit({
  index:
    "my-index-000001,cluster_one:my-index-000001,cluster_two:my-index-000001",
  ccs_minimize_roundtrips: "true",
  query: {
    match: {
      "user.id": "kimchy",
    },
  },
  _source: ["user.id", "message", "http.response.status_code"],
});
console.log(response);
POST /my-index-000001,cluster_one:my-index-000001,cluster_two:my-index-000001/_async_search?ccs_minimize_roundtrips=true
{
  "query": {
    "match": {
      "user.id": "kimchy"
    }
  },
  "_source": ["user.id", "message", "http.response.status_code"]
}

API 返回以下响应

{
  "id": "FklQYndoTDJ2VEFlMEVBTzFJMGhJVFEaLVlKYndBWWZSMUdicUc4WVlEaFl4ZzoxNTU=", 
  "is_partial": true,
  "is_running": true,
  "start_time_in_millis": 1685563581380,
  "expiration_time_in_millis": 1685995581380,
  "response": {
    "took": 1020,
    "timed_out": false,
    "num_reduce_phases": 0,
    "_shards": {
      "total": 10,     
      "successful": 0,
      "failed": 0,
      "skipped": 0
    },
    "_clusters": {    
      "total" : 3,
      "successful" : 0,
      "skipped": 0,
      "running": 3,
      "partial": 0,
      "failed": 0,
      "details": {
        "(local)": {
          "status": "running",
          "indices": "my-index-000001",
          "timed_out": false
        },
        "cluster_one": {
          "status": "running",
          "indices": "my-index-000001",
          "timed_out": false
        },
        "cluster_one": {
          "status": "running",
          "indices": "my-index-000001",
          "timed_out": false
        }
      }
    },
    "hits": {
      "total" : {
          "value": 0,
          "relation": "eq"
      },
      "max_score": null,
      "hits": []
    }
  }
}

异步搜索 ID。

ccs_minimize_roundtrips = true 并且远程集群上的搜索仍在运行时,此部分仅指示本地集群和到目前为止已完成搜索的任何集群的范围内分片数量。仅当搜索完成时,此值才会更新为包括所有集群的分片总数。当 ccs_minimize_roundtrips= false 时,预先知道所有集群的分片总数,并且该值是正确的。

_clusters 部分指示搜索范围中有 3 个集群,并且它们当前都处于“运行中”状态。

如果您在查询仍在运行时查询获取异步搜索端点,您将看到响应的 _clusters_shards 部分的更新,因为每个集群都完成了搜索。

如果设置 ccs_minimize_roundtrips=false,那么您还会看到来自已完成分片(来自任何集群)的部分聚合结果,但在搜索完成之前,不会在“hits”部分中显示任何结果。

如果设置 ccs_minimize_roundtrips=true,那么您还会看到到目前为止已完成的所有集群在响应的“hits”和“aggregations”部分中显示部分结果。(注意:您甚至可以在本地集群完成之前看到来自它的部分聚合结果。)下面的示例显示了 ccs_minimize_roundtrips=true 的情况。

resp = client.async_search.get(
    id="FklQYndoTDJ2VEFlMEVBTzFJMGhJVFEaLVlKYndBWWZSMUdicUc4WVlEaFl4ZzoxNTU=",
)
print(resp)
response = client.async_search.get(
  id: 'FklQYndoTDJ2VEFlMEVBTzFJMGhJVFEaLVlKYndBWWZSMUdicUc4WVlEaFl4ZzoxNTU='
)
puts response
const response = await client.asyncSearch.get({
  id: "FklQYndoTDJ2VEFlMEVBTzFJMGhJVFEaLVlKYndBWWZSMUdicUc4WVlEaFl4ZzoxNTU=",
});
console.log(response);
GET /_async_search/FklQYndoTDJ2VEFlMEVBTzFJMGhJVFEaLVlKYndBWWZSMUdicUc4WVlEaFl4ZzoxNTU=

响应

{
  "id": "FklQYndoTDJ2VEFlMEVBTzFJMGhJVFEaLVlKYndBWWZSMUdicUc4WVlEaFl4ZzoxNTU=",
  "is_partial": true,
  "is_running": true,
  "start_time_in_millis": 1685564911108,
  "expiration_time_in_millis": 1685996911108,
  "response": {
    "took": 11164,
    "timed_out": false,
    "terminated_early": false,
    "_shards": {
      "total": 22,
      "successful": 22,  
      "skipped": 0,
      "failed": 0
    },
    "_clusters": {
      "total": 3,
      "successful": 2,  
      "skipped": 0,
      "running": 1,
      "partial": 0,
      "failed": 0,
      "details": {
        "(local)": {
          "status": "successful",
          "indices": "my-index-000001",
          "took": 2034,
          "timed_out": false,
          "_shards": {
            "total": 10,
            "successful": 10,
            "skipped": 0,
            "failed": 0
          }
        },
        "cluster_one": {
          "status": "successful",
          "indices": "my-index-000001",
          "took": 9039,
          "timed_out": false,
          "_shards": {
            "total": 12,
            "successful": 12,
            "skipped": 0,
            "failed": 0
          }
        },
        "cluster_two": {
          "status": "running",
          "indices": "my-index-000001",
          "timed_out": false
        }
      }
    },
    "hits": {
      "total": {
        "value": 542,  
        "relation": "eq"
      },
      "max_score": 1.7232,
      "hits": [...list of hits here...] 
    }
  }
}

本地集群和远程 cluster_one 集群的所有分片上的搜索已完成。

由于两个集群已完成搜索,因此“成功”集群条目设置为 2,而“正在运行”集群条目减少到 1。随着每个集群完成,_clusters 响应元数据将更新。

到目前为止已完成搜索的命中数。最终命中数只有在所有集群上的搜索完成并合并后才会显示。因此,在搜索完全完成之前,当您调用此端点时,“hits”部分可能会发生变化。

在所有集群上的搜索完成后,查询获取异步搜索端点将显示 _clusters_shards 部分的最终状态,以及命中数和任何聚合结果。

resp = client.async_search.get(
    id="FklQYndoTDJ2VEFlMEVBTzFJMGhJVFEaLVlKYndBWWZSMUdicUc4WVlEaFl4ZzoxNTU=",
)
print(resp)
response = client.async_search.get(
  id: 'FklQYndoTDJ2VEFlMEVBTzFJMGhJVFEaLVlKYndBWWZSMUdicUc4WVlEaFl4ZzoxNTU='
)
puts response
const response = await client.asyncSearch.get({
  id: "FklQYndoTDJ2VEFlMEVBTzFJMGhJVFEaLVlKYndBWWZSMUdicUc4WVlEaFl4ZzoxNTU=",
});
console.log(response);
GET /_async_search/FklQYndoTDJ2VEFlMEVBTzFJMGhJVFEaLVlKYndBWWZSMUdicUc4WVlEaFl4ZzoxNTU=

响应

{
  "id": "FklQYndoTDJ2VEFlMEVBTzFJMGhJVFEaLVlKYndBWWZSMUdicUc4WVlEaFl4ZzoxNTU=",
  "is_partial": false,
  "is_running": false,
  "start_time_in_millis": 1685564911108,
  "expiration_time_in_millis": 1685996911108,
  "completion_time_in_millis": 1685564938727,  
  "response": {
    "took": 27619,
    "timed_out": false,
    "num_reduce_phases": 4,
    "_shards": {
      "total": 28,
      "successful": 28,  
      "skipped": 0,
      "failed": 0
    },
    "_clusters": {
      "total": 3,
      "successful": 3,   
      "skipped": 0,
      "running": 0,
      "partial": 0,
      "failed": 0,
      "details": {
        "(local)": {
          "status": "successful",
          "indices": "my-index-000001",
          "took": 2034,
          "timed_out": false,
          "_shards": {
            "total": 10,
            "successful": 10,
            "skipped": 0,
            "failed": 0
          }
        },
        "cluster_one": {
          "status": "successful",
          "indices": "my-index-000001",
          "took": 9039,
          "timed_out": false,
          "_shards": {
            "total": 12,
            "successful": 12,
            "skipped": 0,
            "failed": 0
          }
        },
        "cluster_two": {
          "status": "successful",
          "indices": "my-index-000001",
          "took": 27550,
          "timed_out": false,
          "_shards": {
            "total": 6,
            "successful": 6,
            "skipped": 0,
            "failed": 0
          }
        }
      }
    },
    "hits": {
      "total": {
        "value": 1067,
        "relation": "eq"
      },
      "max_score": 1.8293576,
      "hits": [...list of hits here...]
    }
  }
}

搜索完成后,将显示 completion_time。

_shards 部分现在更新以显示已跨所有集群搜索了总共 28 个分片,并且全部成功。

_clusters 部分显示所有 3 个集群上的搜索均成功。

跨集群搜索失败

编辑

跨集群搜索期间的失败可能导致两种情况之一

  1. 部分结果(2xx HTTP 状态代码)
  2. 搜索失败(4xx 或 5xx HTTP 状态代码)

在这两种情况下,搜索响应中都会显示失败详细信息。

如果标记为 skip_unavailable=false 的集群不可用、在搜索期间断开连接或所有分片都出现搜索失败,则搜索将失败。在所有其他情况下,失败将导致部分结果。

单个分片上的搜索失败将显示在响应的 _shards 部分和 _clusters 部分中。

失败的搜索将在响应中有一个额外的顶级 errors 条目。

这是一个由于一个集群的一个分片上发生失败而导致部分结果的搜索示例。该搜索将与之前显示的搜索类似。这里使用 _async_search/status 端点来显示完成状态,而不显示命中数。

resp = client.async_search.status(
    id="FmpwbThueVB4UkRDeUxqb1l4akIza3cbWEJyeVBPQldTV3FGZGdIeUVabXBldzoyMDIw",
)
print(resp)
response = client.async_search.status(
  id: 'FmpwbThueVB4UkRDeUxqb1l4akIza3cbWEJyeVBPQldTV3FGZGdIeUVabXBldzoyMDIw'
)
puts response
const response = await client.asyncSearch.status({
  id: "FmpwbThueVB4UkRDeUxqb1l4akIza3cbWEJyeVBPQldTV3FGZGdIeUVabXBldzoyMDIw",
});
console.log(response);
GET /_async_search/status/FmpwbThueVB4UkRDeUxqb1l4akIza3cbWEJyeVBPQldTV3FGZGdIeUVabXBldzoyMDIw

响应

{
  "id": "FmpwbThueVB4UkRDeUxqb1l4akIza3cbWEJyeVBPQldTV3FGZGdIeUVabXBldzoyMDIw",
  "is_partial": true,  
  "is_running": false,
  "start_time_in_millis": 1692106901478,
  "expiration_time_in_millis": 1692538901478,
  "completion_time_in_millis": 1692106903547,
  "response": {
    "took": 2069,
    "timed_out": false,
    "num_reduce_phases": 4,
    "_shards": {
      "total": 28,
      "successful": 27,
      "skipped": 0,
      "failed": 1,
      "failures": [   
        {
          "shard": 1,
          "index": "cluster_two:my-index-000001",
          "node": "LMpUnAu0QEeCUMfg_56sAg",
          "reason": {
            "type": "query_shard_exception",
            "reason": "failed to create query: [my-index-000001][1] exception message here",
            "index_uuid": "4F2VWx8RQSeIhUE-nksvCQ",
            "index": "cluster_two:my-index-000001",
            "caused_by": {
              "type": "runtime_exception",
              "reason": "runtime_exception: [my-index-000001][1] exception message here"
            }
          }
        }
      ]
    },
    "_clusters": {
      "total": 3,
      "successful": 2,
      "skipped": 0,
      "running": 0,
      "partial": 1,   
      "failed": 0,
      "details": {
        "(local)": {
          "status": "successful",
          "indices": "my-index-000001",
          "took": 1753,
          "timed_out": false,
          "_shards": {
            "total": 10,
            "successful": 10,
            "skipped": 0,
            "failed": 0
          }
        },
        "cluster_one": {
          "status": "successful",
          "indices": "my-index-000001",
          "took": 2054,
          "timed_out": false,
          "_shards": {
            "total": 12,
            "successful": 12,
            "skipped": 0,
            "failed": 0
          }
        },
        "cluster_two": {
          "status": "partial",   
          "indices": "my-index-000001",
          "took": 2039,
          "timed_out": false,
          "_shards": {
            "total": 6,
            "successful": 5,
            "skipped": 0,
            "failed": 1   
          },
          "failures": [  
            {
              "shard": 1,
              "index": "cluster_two:my-index-000001",
              "node": "LMpUnAu0QEeCUMfg_56sAg",
              "reason": {
                "type": "query_shard_exception",
                "reason": "failed to create query: [my-index-000001][1] exception message here",
                "index_uuid": "4F2VWx8RQSeIhUE-nksvCQ",
                "index": "cluster_two:my-index-000001",
                "caused_by": {
                  "type": "runtime_exception",
                  "reason": "runtime_exception: [my-index-000001][1] exception message here"
                }
              }
            }
          ]
        }
      }
    },
    "hits": {
    }
  }
}

搜索结果标记为部分,因为至少一个分片搜索失败。

_shards 部分包含分片失败信息。

具有部分结果的集群仍然标记为“partial”。只有当搜索没有返回任何数据时,它们才会标记为状态“skipped”(或“failed”)。

partial 状态已应用于具有部分结果的集群。

显示失败的分片计数。

分片失败也列在 cluster/details 条目下。

这是一个示例,其中 cluster_onecluster_two 在跨集群搜索期间失去了连接。由于 cluster_one 标记为 skip_unavailable=true,因此其状态为 skipped,而由于 cluster_two 标记为 skip_unavailable=false,因此其状态为 failed。由于存在 failed 集群,因此还会出现顶级 error,并且这会返回 500 的 HTTP 状态(未显示)。

如果您希望即使在集群不可用时搜索仍然返回结果,请为所有远程集群设置 skip_unavailable=true

resp = client.async_search.get(
    id="FjktRGJ1Y2w1U0phLTRhZnVyeUZ2MVEbWEJyeVBPQldTV3FGZGdIeUVabXBldzo5NzA4",
)
print(resp)
response = client.async_search.get(
  id: 'FjktRGJ1Y2w1U0phLTRhZnVyeUZ2MVEbWEJyeVBPQldTV3FGZGdIeUVabXBldzo5NzA4'
)
puts response
const response = await client.asyncSearch.get({
  id: "FjktRGJ1Y2w1U0phLTRhZnVyeUZ2MVEbWEJyeVBPQldTV3FGZGdIeUVabXBldzo5NzA4",
});
console.log(response);
GET /_async_search/FjktRGJ1Y2w1U0phLTRhZnVyeUZ2MVEbWEJyeVBPQldTV3FGZGdIeUVabXBldzo5NzA4

响应

{
  "id": "FjktRGJ1Y2w1U0phLTRhZnVyeUZ2MVEbWEJyeVBPQldTV3FGZGdIeUVabXBldzo5NzA4",
  "is_partial": true,
  "is_running": false,
  "start_time_in_millis": 1692112102650,
  "expiration_time_in_millis": 1692544102650,
  "completion_time_in_millis": 1692112106177,
  "response": {
    "took": 3527,
    "timed_out": false,
    "terminated_early": false,
    "_shards": {
      "total": 10,   
      "successful": 10,
      "skipped": 0,
      "failed": 0
    },
    "_clusters": {
      "total": 3,
      "successful": 1,
      "skipped": 1,
      "running": 0,
      "partial": 0,
      "failed": 1,
      "details": {
        "(local)": {
          "status": "successful",
          "indices": "my-index-000001",
          "took": 1473,
          "timed_out": false,
          "_shards": {
            "total": 10,
            "successful": 10,
            "skipped": 0,
            "failed": 0
          }
        },
        "cluster_one": {
          "status": "skipped",   
          "indices": "my-index-000001",
          "timed_out": false,
          "failures": [
            {
              "shard": -1,
              "index": null,
              "reason": {
                "type": "node_disconnected_exception",   
                "reason": "[myhostname1][35.238.149.1:9300][indices:data/read/search] disconnected"
              }
            }
          ]
        },
        "cluster_two": {
          "status": "failed",   
          "indices": "my-index-000001",
          "timed_out": false,
          "failures": [
            {
              "shard": -1,
              "index": null,
              "reason": {
                "type": "node_disconnected_exception",
                "reason": "[myhostname2][35.238.149.2:9300][indices:data/read/search] disconnected"
              }
            }
          ]
        }
      }
    },
    "hits": {
    },
  }
  "error": {  
    "type": "status_exception",
    "reason": "error while executing search",
    "caused_by": {
      "type": "node_disconnected_exception",
      "reason": "[myhostname2][35.238.149.2:9300][indices:data/read/search] disconnected"
    }
  }
}

当发生此类错误时,分片核算通常只是部分完成,因为我们需要能够在每次搜索时从远程集群获取分片信息。

cluster_one 在搜索期间断开连接,并且未返回任何结果。由于它在远程集群配置中标记为 skip_unavailable=true,因此其状态为“skipped”,这不会导致整个搜索失败。

失败列表显示远程集群节点与查询集群断开连接。

cluster_two 状态为“failed”,因为它在远程集群配置中标记为 skip_unavailable=false

当存在“failed”集群时,会包含一个顶级 error 条目。

从跨集群搜索中排除集群或索引

编辑

如果您使用通配符包含大量集群和/或索引,您可以使用集群或索引前面的 - 减号显式排除一个或多个集群或索引。

要排除整个集群,您可以在集群别名前面加上减号,例如:-mycluster:*。排除集群时,您必须在索引位置使用 *,否则将返回错误。

要排除特定的远程索引,您可以在索引前面加上减号,例如 mycluster:-myindex

排除远程集群

以下是如何从使用通配符指定集群列表的跨集群搜索中排除 cluster_three

resp = client.async_search.submit(
    index="my-index-000001,cluster*:my-index-000001,-cluster_three:*",
    query={
        "match": {
            "user.id": "kimchy"
        }
    },
    source=[
        "user.id",
        "message",
        "http.response.status_code"
    ],
)
print(resp)
const response = await client.asyncSearch.submit({
  index: "my-index-000001,cluster*:my-index-000001,-cluster_three:*",
  query: {
    match: {
      "user.id": "kimchy",
    },
  },
  _source: ["user.id", "message", "http.response.status_code"],
});
console.log(response);
POST /my-index-000001,cluster*:my-index-000001,-cluster_three:*/_async_search  
{
  "query": {
    "match": {
      "user.id": "kimchy"
    }
  },
  "_source": ["user.id", "message", "http.response.status_code"]
}

cluster* 表示法自然会包含 cluster_onecluster_twocluster_three。要排除 cluster_three,请在集群名称前使用 - 以及索引位置中的简单通配符 *。这表示您不希望搜索与 cluster_three 进行任何联系。

排除远程索引

假设您要搜索与 my-index-* 匹配的所有索引,但您要排除 cluster_three 上的 my-index-000001。以下是您可以执行此操作的方式

resp = client.async_search.submit(
    index="my-index-000001,cluster*:my-index-*,cluster_three:-my-index-000001",
    query={
        "match": {
            "user.id": "kimchy"
        }
    },
    source=[
        "user.id",
        "message",
        "http.response.status_code"
    ],
)
print(resp)
const response = await client.asyncSearch.submit({
  index: "my-index-000001,cluster*:my-index-*,cluster_three:-my-index-000001",
  query: {
    match: {
      "user.id": "kimchy",
    },
  },
  _source: ["user.id", "message", "http.response.status_code"],
});
console.log(response);
POST /my-index-000001,cluster*:my-index-*,cluster_three:-my-index-000001/_async_search  
{
  "query": {
    "match": {
      "user.id": "kimchy"
    }
  },
  "_source": ["user.id", "message", "http.response.status_code"]
}

这将不会从搜索中排除 cluster_three。它仍然会被联系并告知搜索与 my-index-* 匹配的任何索引,除了 my-index-000001

使用异步搜索进行跨集群搜索,其中 ccs_minimize_roundtrips=false

编辑

ccs_minimize_roundtripsfalse 时,响应的 _shards_clusters 部分的行为会有所不同。

主要区别在于

  1. _shards 部分的总计数将立即准确,因为在搜索开始之前会从所有集群收集分片总数。
  2. _shards 部分将在单个分片的搜索完成后逐步更新,而在最小化往返次数时,分片部分将在本地集群上的分片搜索完成后更新,然后随着每个远程集群报告其完整的搜索结果而更新。
  3. _cluster 部分首先列出其所有分片计数,因为它们也是在查询阶段开始之前获得的。

示例使用与上一节中相同的设置(ccs_minimize_roundtrips=true

resp = client.async_search.submit(
    index="my-index-000001,cluster_one:my-index-000001,cluster_two:my-index-000001",
    ccs_minimize_roundtrips=False,
    query={
        "match": {
            "user.id": "kimchy"
        }
    },
    source=[
        "user.id",
        "message",
        "http.response.status_code"
    ],
)
print(resp)
const response = await client.asyncSearch.submit({
  index:
    "my-index-000001,cluster_one:my-index-000001,cluster_two:my-index-000001",
  ccs_minimize_roundtrips: "false",
  query: {
    match: {
      "user.id": "kimchy",
    },
  },
  _source: ["user.id", "message", "http.response.status_code"],
});
console.log(response);
POST /my-index-000001,cluster_one:my-index-000001,cluster_two:my-index-000001/_async_search?ccs_minimize_roundtrips=false
{
  "query": {
    "match": {
      "user.id": "kimchy"
    }
  },
  "_source": ["user.id", "message", "http.response.status_code"]
}

如果查询花费的时间超过 wait_for_completion_timeout 持续时间,则 API 会返回以下响应(请参阅异步搜索)。

{
  "id": "FklQYndoTDJ2VEFlMEVBTzFJMGhJVFEaLVlKYndBWWZSMUdicUc4WVlEaFl4ZzoxNTU=",
  "is_partial": true,
  "is_running": true,
  "start_time_in_millis": 1685563581380,
  "expiration_time_in_millis": 1685995581380,
  "response": {
    "took": 1020,
    "timed_out": false,
    "_shards": {
      "total": 28,     
      "successful": 0,
      "failed": 0,
      "skipped": 0
    },
    "_clusters": {
      "total" : 3,
      "successful": 0,
      "skipped": 0,
      "running": 3,    
      "partial": 0,
      "failed": 0,
      "details": {    
        "(local)": {
          "status": "running",
          "indices": "my-index-000001",
          "timed_out": false,
          "_shards": {
            "total": 10,
            "successful": 0,
            "skipped": 0,
            "failed": 0
          }
        },
        "cluster_one": {
          "status": "running",
          "indices": "my-index-000001",
          "timed_out": false,
          "_shards": {
            "total": 12,
            "successful": 0,
            "skipped": 0,
            "failed": 0
          }
        },
        "cluster_two": {
          "status": "running",
          "indices": "my-index-000001",
          "timed_out": false,
          "_shards": {
            "total": 6,
            "successful": 0,
            "skipped": 0,
            "failed": 0
          }
        }
      }
    },
    "hits": {
      "total" : {
          "value": 0,
          "relation": "eq"
      },
      "max_score": null,
      "hits": []
    }
  }
}

此处列出了搜索范围内的所有集群中的所有分片。查看此部分和/或 _clusters 部分的更新以监视搜索进度。

_clusters 部分,我们可以看到所有集群都处于“正在运行”状态。

_clusters 部分显示已成功从所有 3 个集群收集了分片信息,并列出了每个集群上的分片总数。

可选远程集群

编辑

默认情况下,如果请求中的远程集群不可用或返回所有分片上的搜索均失败的错误,则跨集群搜索将失败。使用 skip_unavailable 集群设置将特定的远程集群标记为跨集群搜索的可选或必需。

在 Elasticsearch 8.15 中,skip_unavailable 的默认值从 false 更改为 true。在 Elasticsearch 8.15 之前,如果您希望将集群视为跨集群搜索的可选集群,则需要设置该配置。从 Elasticsearch 8.15 开始,您需要设置配置以使集群成为跨集群搜索的必需集群。

如果 skip_unavailabletrue,则跨集群搜索

  • 如果远程集群的节点在搜索期间不可用,则跳过该远程集群。响应的 _clusters.skipped 值包含任何跳过集群的计数,并且响应的 _clusters.details 部分将显示 skipped 状态。
  • 忽略远程集群返回的错误,例如与不可用分片或索引相关的错误。这可能包括与搜索参数相关的错误,例如 allow_no_indicesignore_unavailable
  • 忽略 allow_partial_search_results 参数以及在搜索远程集群时相关的 search.default_allow_partial_results 集群设置。这意味着在远程集群上的搜索可能会返回部分结果。

您可以通过编辑 elasticsearch.yml 配置文件中的 cluster.remote.<cluster_alias> 设置来修改 skip_unavailable 设置。例如

cluster:
    remote:
        cluster_one:
            seeds: 35.238.149.1:9300
            skip_unavailable: false
        cluster_two:
            seeds: 35.238.149.2:9300
            skip_unavailable: true

或者,您可以通过集群更新设置 API 设置 cluster.remote 设置,如此处所示。

当配置为 skip_unavailable: true 的远程集群(例如上面的 cluster_two)在跨集群搜索期间断开连接或不可用时,Elasticsearch 不会将该集群中的匹配文档包含在最终结果中,并且搜索将被视为成功(HTTP 状态 200 OK)。

如果一个集群的至少一个分片提供了搜索结果,则将使用这些结果,并且搜索将返回部分数据。无论远程集群的 skip_unavailable 设置如何,都是如此。(如果使用异步搜索进行跨集群搜索,则 is_partial 字段将设置为 true 以指示部分结果。)

跨集群搜索如何处理网络延迟

编辑

由于跨集群搜索涉及向远程集群发送请求,因此任何网络延迟都可能影响搜索速度。为避免搜索速度缓慢,跨集群搜索提供了两种处理网络延迟的选项

最小化网络往返次数

默认情况下,Elasticsearch 会减少远程集群之间的网络往返次数。这减少了网络延迟对搜索速度的影响。但是,Elasticsearch 无法减少大型搜索请求的网络往返次数,例如那些包含 滚动内部命中的请求。

请参阅考虑是否在跨集群搜索中最小化往返次数的注意事项,了解此选项的工作原理。

不要最小化网络往返次数

对于包含滚动或内部命中的搜索请求,Elasticsearch 会向每个远程集群发送多个传出和传入请求。您也可以通过将 ccs_minimize_roundtrips 参数设置为 false 来选择此选项。虽然通常较慢,但此方法可能适用于具有低延迟的网络。

请参阅不要最小化网络往返次数,了解此选项的工作原理。

矢量图块搜索 API 始终会最小化网络往返次数,并且不包含 ccs_minimize_roundtrips 参数。

近似 kNN 搜索不支持最小化网络往返次数,并将参数 ccs_minimize_roundtrips 设置为 false

选择是否在跨集群搜索中最小化往返次数的考量因素

编辑

最小化往返次数的优势

  1. 对于查询大量分片的跨集群搜索,最小化往返次数选项通常能提供更好的性能。如果被搜索的集群具有较高的网络延迟(例如,地理位置相距遥远),则尤其如此。
  2. 当进行异步跨集群搜索时,即使搜索仍在其他集群上运行,GET _async_search/<search_id> 端点也会提供所有已报告结果的集群的顶部命中结果和聚合结果。换句话说,它会在搜索进行时提供“增量”部分结果。请注意,如果本地集群包含在搜索中,则它具有特殊处理,因为它可以在本地集群上的搜索仍在运行时显示部分聚合(但不显示部分顶部命中结果)。

在使用异步搜索时,不最小化往返次数允许您在搜索仍在运行时,在各个分片完成时(而不是整个集群)获取查询中任何聚合的增量结果,但直到所有集群上的搜索都完成后才显示顶部命中结果。

默认情况下,同步搜索会最小化往返次数,而异步搜索则不会。您可以使用 ccs_minimize_roundtrips 参数覆盖默认值,将其设置为 truefalse,如本文档前面几个示例所示。

最小化网络往返次数

编辑

以下是在您最小化网络往返次数时,跨集群搜索的工作方式。

  1. 您将跨集群搜索请求发送到您的本地集群。该集群中的协调节点接收并解析请求。

    ccs min roundtrip client request

  2. 协调节点向每个集群(包括本地集群)发送单个搜索请求。每个集群独立执行搜索请求,并将自己的集群级设置应用于该请求。

    ccs min roundtrip cluster search

  3. 每个远程集群将其搜索结果发送回协调节点。

    ccs min roundtrip cluster results

  4. 在收集每个集群的结果后,协调节点在跨集群搜索响应中返回最终结果。

    ccs min roundtrip client response

不最小化网络往返次数

编辑

以下是在您不最小化网络往返次数时,跨集群搜索的工作方式。

  1. 您将跨集群搜索请求发送到您的本地集群。该集群中的协调节点接收并解析请求。

    ccs min roundtrip client request

  2. 协调节点向每个远程集群发送“搜索分片”传输层请求,以便它们执行“可以匹配”搜索,以确定应搜索每个集群上的哪些分片。

    ccs min roundtrip cluster search

  3. 每个远程集群将其响应发送回协调节点。此响应包含有关将在其上执行跨集群搜索请求的索引和分片的信息。

    ccs min roundtrip cluster results

  4. 协调节点向每个分片(包括其自身集群中的分片)发送搜索请求。每个分片独立执行搜索请求。

    当不最小化网络往返次数时,搜索的执行方式就好像所有数据都在协调节点的集群中一样。我们建议更新限制搜索的集群级设置,例如 action.search.shard_count.limitpre_filter_shard_sizemax_concurrent_shard_requests,以考虑这一点。如果这些限制太低,则搜索可能会被拒绝。

    ccs dont min roundtrip shard search

  5. 每个分片将其搜索结果发送回协调节点。

    ccs dont min roundtrip shard results

  6. 在收集每个集群的结果后,协调节点在跨集群搜索响应中返回最终结果。

    ccs min roundtrip client response

支持的跨集群搜索配置

编辑

在 8.0+ 版本中,Elastic 支持从本地集群到运行以下版本的远程集群的搜索

  • 上一个次要版本。
  • 相同版本。
  • 同一主要版本中的较新次要版本。

Elastic 还支持从运行主要版本最后一个次要版本的本地集群到运行以下主要版本中任何次要版本的远程集群的搜索。例如,本地 7.17 集群可以搜索任何远程 8.x 集群。

远程集群版本

本地集群版本

6.8

7.1–7.16

7.17

8.0

8.1

8.2

8.3

8.4

8.5

8.6

8.7

8.8

8.9

8.10

8.11

8.12

8.13

8.14

8.15

8.16

8.17

6.8

Yes

Yes

Yes

No

No

No

No

No

No

No

No

No

No

No

No

No

No

No

No

No

No

7.1–7.16

Yes

Yes

Yes

No

No

No

No

No

No

No

No

No

No

No

No

No

No

No

No

No

No

7.17

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

8.0

No

No

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

8.1

No

No

No

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

8.2

No

No

No

No

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

8.3

No

No

No

No

No

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

8.4

No

No

No

No

No

No

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

8.5

No

No

No

No

No

No

No

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

8.6

No

No

No

No

No

No

No

No

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

8.7

No

No

No

No

No

No

No

No

No

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

8.8

No

No

No

No

No

No

No

No

No

No

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

8.9

No

No

No

No

No

No

No

No

No

No

No

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

8.10

No

No

No

No

No

No

No

No

No

No

No

No

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

8.11

No

No

No

No

No

No

No

No

No

No

No

No

No

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

8.12

No

No

No

No

No

No

No

No

No

No

No

No

No

No

Yes

Yes

Yes

Yes

Yes

Yes

Yes

8.13

No

No

No

No

No

No

No

No

No

No

No

No

No

No

No

Yes

Yes

Yes

Yes

Yes

Yes

8.14

No

No

No

No

No

No

No

No

No

No

No

No

No

No

No

No

Yes

Yes

Yes

Yes

Yes

8.15

No

No

No

No

No

No

No

No

No

No

No

No

No

No

No

No

No

Yes

Yes

Yes

Yes

8.16

No

No

No

No

No

No

No

No

No

No

No

No

No

No

No

No

No

No

Yes

Yes

Yes

8.17

No

No

No

No

No

No

No

No

No

No

No

No

No

No

No

No

No

No

No

Yes

Yes

对于 EQL 搜索 API,如果本地和远程集群的版本早于 7.17.7(包括)或 8.5.1(包括),则它们必须使用相同的 Elasticsearch 版本。

例如,本地 8.0 集群可以搜索远程 7.17 或任何远程 8.x 集群。但是,不支持从本地 8.0 集群到远程 7.16 或 6.8 集群的搜索。

仅支持所有搜索集群中都存在的功能。在不支持该功能的远程集群上使用功能会导致未定义的行为。

使用不支持的配置的跨集群搜索可能仍然可以工作。但是,Elastic 不会测试此类搜索,并且不保证其行为。

确保跨集群搜索支持

编辑

确保您的集群支持跨集群搜索的最简单方法是使每个集群都使用相同版本的 Elasticsearch。如果您需要维护具有不同版本的集群,则可以

  • 维护一个专用于跨集群搜索的集群。使该集群保持在搜索其他集群所需的最早版本。例如,如果您有 7.17 和 8.x 集群,则可以维护一个专用的 7.17 集群,以用作跨集群搜索的本地集群。
  • 使每个集群之间的版本差异不超过一个次要版本。这使您可以在运行跨集群搜索时使用任何集群作为本地集群。

升级期间的跨集群搜索

编辑

您仍然可以在本地集群上执行滚动升级时搜索远程集群。但是,本地协调节点的“从...升级”和“升级到...”版本必须与远程集群的网关节点兼容。

不支持在同一集群中运行多个 Elasticsearch 版本(超出升级期间的时间)。

有关升级的更多信息,请参阅升级 Elasticsearch