空间索引刚接触容易觉得抽象,简单来说,就是地图数据的快速检索目录。外卖APP打开,附近三公里店铺瞬间加载,背后就依靠空间索引。没有这套机制,系统需要遍历全城几十万店铺逐个计算距离,查询等待时间会很长。
普通数据库索引大家比较熟悉,B树、哈希表依靠数值、字符串排序实现快速检索。这套思路放到二维平面就行不通。经纬度属于两个独立维度,没办法简单按顺序排列。空间查询大多不是精准匹配,更多是范围查询,矩形范围内坐标点、最近目标点位、线路覆盖区域,普通索引处理这类查询效率很低。
所以需要专门的数据结构,下面几种方案,适用场景各不相同。

网格索引实现最简单,把地图切割成尺寸一致方格,坐标点归属对应格子记录。查询先确定目标范围覆盖哪些方格,仅检索对应格子内数据。代码编写难度低,二维数组搭配链表就可以搭建。缺点明显,数据分布不均性能暴跌,市中心单格子存放数万点位,郊区格子空白,查询退化成遍历链表。多级网格,大格子嵌套小格子可以缓解,但层级增加,维护成本上涨。
四叉树采用递归分割思路。整块区域作为根节点,数据量超出阈值切分成四个象限,每个象限持续拆分,直到叶子节点数据量合适。相比网格索引,不均匀数据场景适应性更好,数据稠密区域树更深,稀疏区域树浅。局限在于二维场景合适,三维及以上扩展困难,频繁增删数据造成树失衡,需要定期重建。
R树工程落地最多。核心逻辑,最小外接矩形包裹邻近数据,父节点矩形覆盖全部子节点矩形,多层嵌套。查询从根节点筛选,无交集分支直接跳过,减少检索量,范围查询、邻近点查询性能优秀。缺陷是矩形容易重叠,重叠越多剪枝效果越差,极端情况退化为全表扫描。衍生R+树、R*树,依靠不同分裂策略降低矩形重叠问题。
Java开发实现空间索引,直接使用成熟库最省事。JTS,Java拓扑套件,属于LocationTech项目,功能全面,几何对象、空间关系判断、索引结构都内置。自带STRtree属于R树变体,批量加载性能好,适合变动少的数据场景。使用简单,将几何对象存入索引,传入查询范围,返回相交数据。
GeoTools基于JTS封装更多地理处理能力,支持读取Shapefile、坐标转换。但包体积大,依赖多,如果只需要空间索引,单独JTS足够。
不想引入第三方依赖,可以手写简易网格索引。几十行代码就能跑,HashMap存储,key由格子行列拼接字符串,value存放格子内对象列表。查询计算目标范围覆盖格子,取出列表合并去重。数据量不大、点位分布均匀,这种简单方案够用,省去依赖。百万级别数据,优先JTS的STRtree,底层内存管理、查询优化经过打磨。
工程开发有个需要留意的坑。空间索引大多存放内存,服务器重启或者进程崩溃,索引丢失,需要从数据库重新加载。大数据场景重建索引耗时久。常用方案持久化索引到磁盘,或者Redis缓存索引。索引建好不是永久不变,数据变更要同步更新,否则查询结果过期。R树插入删除开销高于查询,高频写入场景维护索引消耗资源,甚至超过查询节省时间。可以批量更新数据,或者直接选用自带空间索引的数据库,比如PostGIS,交给数据库维护索引。
选型参考数据规模、更新频率、查询方式。几万条数据手写网格索引就行。百万级数据,JTS的STRtree稳定。数据量更大,或者需要对接数据库,PostGIS这类空间数据库更合适。不存在万能方案,满足业务需求就是最优选择。