Es geo索引超时
最近好几个兄弟在群里吐槽,说搞地理围栏查询的时候,Es老是报超时。我也没多想,直接去看了下日志,好家伙,那查询耗时简直能让人喝杯茶再回来。这问题其实挺常见的,但很多人一上来就怪硬件,怪集群,其实大部分时候是配置或者写法的问题。今天我就把压箱底的干货掏出来,希望能帮你们少走弯路。
先说个真实案例。上周有个做物流轨迹的项目,客户要在全国范围内查某个车辆过去一小时经过的所有电子围栏。数据量大概两千万条,用的都是geo_point类型。一开始查询直接卡死,超时设置哪怕调到30秒也扛不住。我接手后,第一反应不是加机器,而是看索引结构。
第一步,检查你的mapping。很多新手为了省事,直接让Es自动创建索引,结果geo_point字段被识别成了text或者keyword,或者精度丢失严重。你必须在创建索引时显式声明type为geo_point,并且设置distance_type为arc,这样计算距离才准,虽然慢点,但比算错了好。如果已经建错了索引,别慌,新建一个正确的索引,用reindex工具把数据迁过去。这个过程大概需要几个小时,视数据量而定,但一劳永逸。
第二步,优化查询语句。这是重灾区。很多人喜欢用match_all或者宽泛的bool查询,然后在里面嵌套geo_distance。记住,geo查询一定要配合filter上下文,别用query上下文,除非你真的需要相关性评分。filter是不走评分的,速度快得多。比如,你要查半径5公里内的点,写法应该是:
{
"bool": {
"filter": [
{
"geo_distance": {
"distance": "5km",
"location": {
"lat": 39.9042,
"lon": 116.4074
}
}
}
]
}
}
注意,location字段要是geo_point类型。如果你用的是geo_shape,那更要注意,geo_shape的查询性能比geo_point差一个量级,除非你有复杂的几何形状需求,否则尽量用geo_point。
第三步,调整超时设置。Es默认的查询超时是30秒,但对于地理查询来说,这可能不够,也可能太多。如果数据量极大,30秒都查不完,说明你的集群负载太高或者索引设计有问题。这时候不要盲目增加超时时间,而是先看看Shard的数量。如果Shard太大,比如单个Shard超过50GB,查询就会变慢。建议将Shard大小控制在10-30GB之间。如果Shard太多,小文件过多,也会拖慢速度。可以通过split或者merge来调整。
第四步,利用缓存。Es的查询缓存和过滤器缓存很重要。确保你的查询语句是幂等的,这样Es才能有效缓存结果。对于频繁查询的地理围栏,可以考虑将结果缓存到Redis或者Memcached中,Es只负责提供底层数据。这样能大幅减轻Es的压力,避免Es geo索引超时的问题再次发生。
还有几个坑要注意。别在geo查询中使用通配符或者正则表达式,这会让Es回退到全表扫描,性能极差。另外,如果你的数据量特别大,考虑使用滚动索引(Rolling Index),按时间分片,查询时只查最近的数据,历史数据归档。
最后,说点实在的。Es geo索引超时不是无解的,关键是要找到瓶颈。是数据量太大?还是查询写法不对?或者是硬件资源不足?一步步排查,总能解决。如果你实在搞不定,或者需要更专业的调优建议,欢迎随时找我聊聊。毕竟,这行水很深,多个人多份力,大家一起进步嘛。
本文关键词:Es geo索引超时