StarRocks ST_Distance_Sphere 函数详解:计算地球表面两点间球面距离
【免费下载链接】starrocksThe world's fastest open query engine for sub-second analytics both on and off the data lakehouse. With the flexibility to support nearly any scenario, StarRocks provides best-in-class performance for multi-dimensional analytics, real-time analytics, and ad-hoc queries. A Linux Foundation project.项目地址: https://gitcode.com/GitHub_Trending/st/starrocks
ST_Distance_Sphere 是 StarRocks 提供的空间地理函数之一,用于计算地球表面上两个经纬度坐标点之间的球面距离(以米为单位),是 LBS(基于位置的服务)、配送调度、出行导航、地理围栏等场景中做"两点间距离筛选与排序"的高频工具。读完本文,你将掌握该函数的完整语法、参数约定、返回值语义、NULL 与非法坐标处理行为,并能结合源码了解其在 StarRocks 前端(FE)注册与后端(BE)基于 S2 地理库的实现原理,直接在真实表上完成近邻距离查询。
函数概述
ST_Distance_Sphere属于 StarRocks 空间函数(Spatial Functions)家族,与st_point、st_x、st_y、st_astext、st_circle、st_contains、st_polygon等函数并列,完整清单可见 docs/en/sql-reference/sql-functions/spatial-functions 目录。其核心职责是:输入两组经纬度坐标,输出两点在大地水准球面上的距离,单位为米(meters)。
从源码注册表看,该函数在前端被显式声明为内置函数:在 fe/fe-core/src/main/java/com/starrocks/catalog/FunctionSet.java 中,public static final String ST_DISTANCE_SPHERE = "st_distance_sphere";与其余 Geo 函数一同注册,说明它是一个经过正式声明、可被 SQL 解析与优化器处理的系统内置函数,而非 UDF。
语法与参数说明
函数签名如下(原始文档定义,注意该签名的最后一个形参是y_lat,即 Y 点的纬度):
DOUBLE ST_Distance_Sphere(DOUBLE x_lng, DOUBLE x_lat, DOUBLE y_lng, DOUBLE y_lat)| 参数 | 类型 | 含义 |
|---|---|---|
x_lng | DOUBLE | X 点的经度(Longitude) |
x_lat | DOUBLE | X 点的纬度(Latitude) |
y_lng | DOUBLE | Y 点的经度(Longitude) |
y_lat | DOUBLE | Y 点的纬度(Latitude) |
返回值:DOUBLE类型,单位为米。
几点使用约定:
- 经度范围:合法经度在
[-180, 180]区间内;纬度范围:合法纬度在[-90, 90]区间内。超出合法范围的坐标会被判定为无效输入。 - 参数既可以是字面量常量,也可以是表中列的值,因此可以直接参与
SELECT投影、WHERE过滤以及ORDER BY ... LIMIT的近邻排序查询。 - 经纬度使用十进制角度(degrees)传入,函数内部会将其转换为弧度/球面坐标进行计算。
参数顺序是常见的踩坑点
该函数接收的是经度在前、纬度在后的顺序(lng, lat, lng, lat),这与许多 GIS 工具中"纬度、经度"的书写习惯相反,也与st_point等函数约定的 X/Y 语义保持一致(在 StarRocks 的地理模型中,x对应经度、y对应纬度)。若将经纬度顺序写反,函数不会报错,但会计算出完全错误(通常偏大)的距离,这是实际业务中最常见的问题来源。建议在调用前对输入数据做一次坐标顺序校验。
使用示例
1. 字面量常量调用
原始文档给出的示例是在两个北京坐标点之间计算距离:
MySQL > select st_distance_sphere(116.35620117, 39.939093, 116.4274406433, 39.9020987219); +----------------------------------------------------------------------------+ | st_distance_sphere(116.35620117, 39.939093, 116.4274406433, 39.9020987219) | +----------------------------------------------------------------------------+ | 7336.9135549995917 | +----------------------------------------------------------------------------+结果7336.9135549995917表示这两个经纬度点之间的球面距离约为7336.91 米,与实际地理常识相符(北京城区内两点约 7.3 公里)。
2. 结合表列计算点对距离
在实际业务中,更常见的是基于表中存储的坐标列进行计算。例如有一张存储门店坐标的表stores(id, name, lng, lat),可以用如下方式计算每个门店与某个参考点(如用户当前位置116.40, 39.90)之间的距离:
SELECT name, ST_Distance_Sphere(116.40, 39.90, lng, lat) AS distance_m FROM stores ORDER BY distance_m ASC LIMIT 10;3. 结合 WHERE 过滤实现半径圈选
筛选出距参考点 5 公里以内的所有记录:
SELECT id, name, ST_Distance_Sphere(116.40, 39.90, lng, lat) AS distance_m FROM stores WHERE ST_Distance_Sphere(116.40, 39.90, lng, lat) <= 5000;这类"距离圈选 + 距离排序"的组合正是该函数在 LBS 场景下的典型用法。
返回值与异常处理行为
结合后端实现源码 be/src/exprs/geo_functions.cpp,可以确认以下运行时行为:
- 任一参数为 NULL 时,该行结果返回 NULL。实现中先通过
ColumnViewer读取四列值,并对每一行依次检查x_lng/x_lat/y_lng/y_lat是否为 NULL,只要有一个为 NULL 就append_null()(见源码第 255-258 行)。 - 坐标非法时返回 NULL。底层
GeoPoint::st_distance_sphere在校验坐标失败时返回false,上层随即追加 NULL(见源码第 266-269 行),而不是抛错中止查询。 - 同一坐标点(两点重合)时距离为 0,这一点有单元测试佐证:在 be/test/exprs/geography_functions_test.cpp 的
st_distance_sphereTest中,四参数全部传入0.0,断言结果等于0。 - 函数是向量化实现:通过
ColumnBuilder/ColumnViewer对整列数据逐行批量计算,并利用ColumnHelper::is_all_const(columns)判断常量列以优化执行(见源码第 252-274 行),在大量坐标点参与计算时具备良好的列式执行性能。
源码级原理:基于 S2 地理库的球面距离计算
该函数的真正计算逻辑位于 be/src/geo/geo_types.cpp,仅 12 行核心代码,全部依赖 Google S2 Geometry 库:
bool GeoPoint::st_distance_sphere(double x_lng, double x_lat, double y_lng, double y_lat, double* result) { S2LatLng x = S2LatLng::FromDegrees(x_lat, x_lng); if (!x.is_valid()) { return false; } S2LatLng y = S2LatLng::FromDegrees(y_lat, y_lng); if (!y.is_valid()) { return false; } *result = S2Earth::ToMeters(x.GetDistance(y)); return true; }其计算链路可以拆解为三步:
- 构建球面坐标点:
S2LatLng::FromDegrees(lat, lng)将十进制的经纬度对转换为 S2 的球面经纬度表示。注意此处传参顺序是(纬度, 经度),与 SQL 层(经度, 纬度)的参数顺序刚好相反,说明 SQL 层到地理内核层之间存在一次坐标顺序转换。 - 合法性校验:
S2LatLng::is_valid()校验纬度是否在[-90, 90]、经度是否在[-180, 180]内;任一坐标不合法则函数返回false,上层将其折叠为 SQL 层面的 NULL。 - 球面距离换算:
x.GetDistance(y)返回两点间沿球面的角距离(弧度/角度),再经S2Earth::ToMeters乘以地球平均半径换算为以米为单位的实际距离。这正是函数名中 "Sphere" 的含义——它基于球面模型(而非更精确但更昂贵的椭球大地水准面模型)计算,精度满足绝大多数 LBS 业务需求。
函数声明位于 be/src/geo/geo_types.h,向量化入口GeoFunctions::st_distance_sphere在 be/src/exprs/geo_functions.h 中通过DEFINE_VECTORIZED_FN宏注册,与 FE 侧 FunctionSet.java 的字符串名称一一对应,构成一条完整的"SQL 函数名 → 前端注册 → 后端向量化执行 → S2 地理计算"的调用链。
注意事项与适用边界
- 球面模型近似:函数采用球面模型计算距离,未考虑地球椭球形状与海拔,在跨洲际长距离场景下与高精度大地测量结果存在米级到百米级的误差;城市级、区域级近邻计算完全适用。
- 经纬度顺序:牢记"先经度、后纬度"的入参顺序,避免与常见 GIS 工具的"先纬度、后经度"约定混淆。
- NULL 语义:任何 NULL 或越界坐标都会导致结果为 NULL,业务上可用
COALESCE/IFNULL处理,或提前清洗脏数据。 - 性能提示:在
WHERE中直接调用该函数做半径过滤时,无法直接利用空间索引,属于全表逐行计算;数据量极大时建议配合分区裁剪或先按经纬度粗粒度矩形过滤,再做精确球面距离校验。
参考与延伸阅读
- 函数官方定义文档:docs/en/sql-reference/sql-functions/spatial-functions/st_distance_sphere.md
- 同目录其他空间函数:
st_point、st_x、st_y、st_astext、st_circle、st_contains等,见 spatial-functions 目录 - 后端向量化实现:be/src/exprs/geo_functions.cpp
- S2 球面距离核心算法:be/src/geo/geo_types.cpp
- 单元测试:be/test/exprs/geography_functions_test.cpp
- FE 端函数注册:fe/fe-core/src/main/java/com/starrocks/catalog/FunctionSet.java
【免费下载链接】starrocksThe world's fastest open query engine for sub-second analytics both on and off the data lakehouse. With the flexibility to support nearly any scenario, StarRocks provides best-in-class performance for multi-dimensional analytics, real-time analytics, and ad-hoc queries. A Linux Foundation project.项目地址: https://gitcode.com/GitHub_Trending/st/starrocks
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考