那天凌晨两点,服务器报警。
说是定位功能挂了。
用户搜附近的店,结果要么空,要么全是错的。
我盯着屏幕,头发都要抓秃了。
明明代码没改,配置也没动。
怎么就突然失灵了呢?
其实,这锅不能全甩给代码。
很多人用elasticsearch5 geo的时候,都忽略了一个细节。
就是那个该死的坐标系转换。
你以为是经纬度随便填填就行?
天真。
ES5对geo_point的处理,比你想的严谨得多。
我记得第一次搞这个的时候,
是在一个老项目上重构。
数据源是GPS模块直接传的。
我以为直接塞进字段里,
就能跑通near query。
结果呢?
查出来的距离,
跟实际地图上的差了十万八千里。
那一刻,我真想砸键盘。
后来我去翻了官方文档,
还有几个大牛的技术博客。
才发现问题出在哪。
ES5默认用的是WGS84坐标系。
但国内很多地图服务商,
比如高德、百度,
用的是GCJ-02或者BD-09。
这中间的偏差,
可不是几米那么简单。
你要是直接把百度地图的坐标,
扔进ES里做geo查询,
那结果简直就是灾难。
所以,在决定用elasticsearch5 geo之前,
你得先搞清楚你的数据源。
是原始GPS数据?
还是经过纠偏后的地图数据?
如果是后者,
那你必须在入库前,
做一次坐标转换。
别嫌麻烦,
这一步省不得。
我后来写了一个简单的转换脚本,
在数据入库前,
先把GCJ-02转成WGS84。
然后再存入ES。
这样查出来的结果,
就跟地图上的位置严丝合缝。
除了坐标系,
还有一个坑,
就是字段的映射类型。
很多人喜欢偷懒,
直接设成text或者keyword。
然后尝试加geo_point属性。
这是大忌。
在ES5里,
geo_point字段,
必须显式声明类型。
而且,
它的存储方式,
也有讲究。
你可以存成lat,lon的字符串,
也可以存成数组,
或者对象。
我推荐用对象形式。
因为这样可读性高,
调试起来也方便。
比如:
{
"location": {
"lat": 39.9042,
"lon": 116.4074
}
}
这样写,
虽然多打几个字符,
但心里踏实。
别小看这几行配置,
它决定了你后续查询的效率。
说到查询,
near query是标配。
但你要记得,
ES5的near query,
是基于球面几何计算的。
它假设地球是个完美的球体。
虽然有点误差,
但对于大多数业务场景,
足够了。
如果你追求极致的精度,
比如物流追踪,
那可能需要引入插件,
或者自己算。
但一般情况下,
别折腾那些花里胡哨的。
稳定,
才是第一位的。
我还遇到过一种情况,
就是数据量大的时候,
查询变慢。
这时候,
别急着加索引。
先看看你的查询语句。
是不是用了太多过滤条件?
geo查询本身,
计算量就不小。
再加上其他字段过滤,
CPU直接飙满。
我的建议是,
先过滤,
再查geo。
比如,
先限定城市,
或者商圈。
缩小范围,
再算距离。
这样性能提升,
立竿见影。
其实,
搞懂elasticsearch5 geo,
也没那么玄乎。
就是多踩几个坑,
多查几遍文档。
别怕报错,
报错是最好的老师。
我现在回头看,
那些熬夜调参的日子,
虽然痛苦,
但也让我对ES的理解,
深了一层。
技术这东西,
就是这样,
越琢磨,
越有意思。
希望你也能少走弯路,
早点搞定这个问题。
毕竟,
早点下班,
才是硬道理。