原理(5.0版本)
摘自 Elasticsearch 在地理信息空间索引的探索和演进-51CTO.COM
方案优化的探索是没有没有止境的,Lucene的核心工程师 Michael McCandless受到论文《Bkd-Tree: A Dynamic Scalable kd-Tree》启发,基于BKD tree再次升级了地理位置数据索引建模和查询功能。这个数据结构不仅仅是用于解决地理位置查询问题,更是数值类数据索引建模的通用方案。它可以处理一维的数值,从byte到BigDecimal, IPv6地址等等;它也可以处理二维乃至于N维的数据检索问题。
LUCENE-6825
This can be used for very fast 1D range filtering for numerics, removing the 8 byte (long/double) limit we have today, so e.g. we could efficiently support BigInteger, BigDecimal, IPv6 addresses, etc.
It can also be used for > 1D use cases, like 2D (lat/lon) and 3D (x/y/z with geo3d) geo shape intersection searches.
...
It should give sizable performance gains (smaller index, faster searching) over what we have today, and even over what auto-prefix with efficient numeric terms would do.
在前面的版本中,对于数值区间查询的处理思路本质上都是term匹配,通过前缀实现了一个term管理一个区间,从而降低了区间查询需要遍历的term数量。而从ES 5.0版本开始,彻底优化了数值查询(从一维到N维),其底层是Lucene 6.0版本实现的BKD tree的独立索引。其实现不仅降低了内存开销,而且提升了检索和索引速度。
关于bkd-tree的原理,其大体思路如下。在面对数值查询区间查询的问题上,大体分为两个层次:
- 【优化内存查询】:BST(binary-search-tree) > Self-balanced BST > kd-tree。
- 【优化外存(硬盘)查询】:B-tree > K-D-B-tree > BKD tree。
kd-tree其实就是多维的BST。例如:
【数据存储】:BKD tree的核心思路是非常简单的,将N维点集合形成的矩形空间(southWest,northEast)递归分割成更小的矩形空间。跟常见的kd-tree不同,当分割到网格区域里面坐标点的数量小于一定数量(比如1024)就停止了。
例如:
通过区域的分割,确保每个区域POI的数量大致相等。
【数据查询】:搜索的时候,就不再是像Quadtree从整个世界开始定位,而是基于当前的点集合形成的空间来查找。例如以geo_distance查询为例。
其流程如下:
第一步: 中心点坐标+半径生成一个矩形(shape boundary)。这一步是常规操作了,前面的版本也都是这么做的。
第二步:该矩形跟BKD tree 叶子节点形成的矩形(cell)进行intersect运算,所谓intersect运算,就是计算两个矩形的位置关系:相交、内嵌还是不相关。query和bkd-tree形成的区域有三种关系。
- 对于CELL_CROSSES_QUERY,如果是叶子节点,则需要判断cell中的每个POI是否符合query的查询条件;否则查询子区间;
- 对于CELL_OUTSIDE_QUERY,直接略过;
- 对于CELL_INSIDE_QUERY,整个cell中的POI都满足查询条件。
核心代码:LatLonPoint/LatLonPointDistanceQuery