elasticsearch-py查询远慢于curl等价请求的原因排查
这种情况我之前排查过好几次,大概率是elasticsearch-py的默认配置和curl的请求逻辑差异导致的,下面是几个最常见的原因和解决思路:
1. 自动节点嗅探(Sniffing)拖慢初始化过程
elasticsearch-py 5.x版本默认会在客户端初始化时开启节点嗅探(sniff_on_start=True)——也就是客户端会先向指定的es-host发送请求,获取集群中所有节点的地址,后续请求会随机分发到这些节点上。如果你的ES集群节点较多,或者es-host是负载均衡器、网络存在延迟,这个嗅探过程可能会耗时极久,甚至卡住。
而curl请求是直接向指定节点发送查询,没有这个额外的嗅探步骤,所以能瞬间返回。
解决方法:初始化Elasticsearch客户端时显式关闭嗅探:
es = Elasticsearch(["es-host:9200"], sniff_on_start=False)
如果需要保留嗅探功能,可以调整嗅探超时时间,避免长时间等待:
es = Elasticsearch(["es-host:9200"], sniff_on_start=True, sniff_timeout=10)
2. 请求超时与重试机制的差异
elasticsearch-py默认请求超时时间是10秒,且遇到分片未响应等情况时会自动重试(默认重试3次)。如果集群临时压力大,这个重试+等待超时的过程可能累积成很长的耗时。而curl默认没有重试机制,一旦收到响应就返回,哪怕部分分片未成功(这种情况很少见)。
排查方法:开启日志查看请求过程中的超时或重试记录:
import logging logging.basicConfig(level=logging.INFO) es = Elasticsearch(["es-host:9200"])
如果确实是超时问题,可以调整超时和重试参数:
es = Elasticsearch(["es-host:9200"], request_timeout=30, max_retries=1)
3. 响应结果的处理方式不同
虽然curl和elasticsearch-py默认都只返回前10条结果(size=10),但elasticsearch-py会把响应JSON解析成Python字典/对象,而curl只是输出原始JSON字符串。如果单条文档体积极大,Python的JSON解析可能会比curl的解析耗时更长,但这种情况一般只会造成毫秒级差异,不太会达到28分钟,优先级较低。
验证方法:尝试获取原始响应跳过解析步骤:
response = es.search(index="some_index", doc_type="some_type", body={"query": {"term": {"day": "2018_02_04"}}}, raw=True)
如果raw=True时速度明显变快,说明是解析过程导致的延迟。
4. 连接池配置问题
elasticsearch-py默认用连接池管理HTTP连接,第一次请求时需要创建连接池,而curl是单次请求创建连接。不过连接池创建的耗时通常在毫秒级,不太可能导致28分钟的延迟,不过也可以尝试调整:
from elasticsearch.connection.http_urllib3 import Urllib3HttpConnection es = Elasticsearch(["es-host:9200"], connection_class=Urllib3HttpConnection, maxsize=10)
总结
最可能的原因是节点嗅探导致的初始化延迟,建议先尝试关闭sniff_on_start再测试。如果问题依然存在,再逐步排查超时、重试或响应解析的问题。
内容的提问来源于stack exchange,提问作者Metropolis

