greatCircleDistance 在 ClickHouse 中:避免全表扫描
Source: Dev.to

问题
在处理位置数据时,一个常见的问题是:
如何计算存储在数据库中的两个坐标之间的距离?
如果你使用 ClickHouse,无需在数据库外部处理——它内置了相应的函数。
正确工具:greatCircleDistance
greatCircleDistance(lat1, lon1, lat2, lon2)
它返回地球上两点之间的最短距离,单位为 米。
示例
SELECT greatCircleDistance(13.0827, 80.2707, 12.9716, 77.5946) AS distance_meters;
上述查询返回金奈(Chennai)和班加罗尔(Bangalore)之间的距离。
看似简单… 但有陷阱
一个天真的查询,例如:
SELECT city
FROM locations
WHERE greatCircleDistance(lat, lon, 13.0827, 80.2707) < 5000;
在大表上可能会触发全表扫描。
为什么会这样
ClickHouse 的索引是 稀疏的,旨在进行 范围裁剪。它们在以下条件下表现良好:
WHERE lat BETWEEN x AND y
但在以下条件下则不行:
WHERE greatCircleDistance(lat, lon, x, y) < 5000
因为函数是对列值进行计算,ClickHouse 无法利用索引高效跳过数据。
更佳方案(你实际上应该做的)
先使用边界框过滤缩小数据集,然后再应用精确的距离计算。
边界框过滤
SELECT city
FROM locations
WHERE lat BETWEEN (13.0827 - 0.05) AND (13.0827 + 0.05)
AND lon BETWEEN (80.2707 - 0.05) AND (80.2707 + 0.05)
AND greatCircleDistance(lat, lon, 13.0827, 80.2707) < 5000;
边界框是一种近似,它在执行精确的 greatCircleDistance 检查之前先缩小搜索空间。
为什么有效
lat BETWEEN …→ 使用索引lon BETWEEN …→ 进一步减少行数greatCircleDistance→ 仅对已过滤的子集应用
因此,查询避免了对整张表的扫描。
实际使用场景
- 配送半径过滤
- 查找附近的用户
- 基于地理位置的分析
- 共享出行系统
一个重要的注意点
- 坐标必须是 度(而非弧度)。
- 顺序始终是
(lat, lon)。调换顺序会得到错误结果。
结语
greatCircleDistance 功能强大,但盲目使用会影响性能。在 ClickHouse 中,查询设计往往比调用的函数更重要。了解 何时以及如何 使用 greatCircleDistance——通常在进行友好索引的预过滤之后——可以确保地理查询高效且可扩展。