thinkphp 6.x 中计算两点经纬度距离应优先使用 mysql 5.7+ 的 st_distance_sphere(支持空间索引、精度高),php 层可手写 haversine 公式但须统一单位为弧度、地球半径取 6371 km;地址转经纬度需调用高德/百度等第三方 api 并做好缓存、异常兜底与异步请求;mysql 表需增设 point 字段并建立 spatial index,否则查询性能骤降;db::raw 写 haversine 易因未转弧度或参数错位导致结果不准,st_distance_sphere 更稳妥。

ThinkPHP 6.x 中如何计算两点经纬度距离
直接用 think\helper\Str 或数据库函数更高效,TP 自身不提供内置的地理距离计算方法。别在 Db::raw() 里硬写 Haversine 公式却忽略索引失效问题——这是最常踩的坑。
推荐两种稳妥方式:
- 数据库层计算(MySQL 5.7+):用
ST_Distance_Sphere,支持空间索引,查得快、精度高 - PHP 层计算(兼容低版本或简单场景):手写 Haversine,但必须注意单位统一(度 → 弧度)、地球半径取值(6371 km 更常用)
示例(MySQL 空间查询):
SELECT id, name,
ST_Distance_Sphere(
POINT(116.48, 39.92),
POINT(longitude, latitude)
) AS distance
FROM shops
WHERE ST_Distance_Sphere(
POINT(116.48, 39.92),
POINT(longitude, latitude)
) <h3>ThinkPHP 中怎么把地址转成经纬度(逆地理编码)</h3><p>TP 不处理地址解析,得调第三方 API。高德、百度、腾讯地图都提供 HTTP 接口,关键不是“怎么发请求”,而是“怎么缓存 + 防错”。</p><p>常见错误:</p>
- 每次查地址都实时调 API → 耗时、限流、白名单没配好直接 403
- 没对空结果或异常响应做兜底 →
NULL写进数据库,后续距离计算全崩 - 用
file_get_contents同步阻塞请求 → 页面卡死
实操建议:
- 用
think\Http或guzzlehttp/guzzle发异步/带超时的 GET - 查前先查本地缓存表(
address_cache),字段含address、lng、lat、expires_at - 失败时返回默认坐标(如城市中心点),并记录日志,避免中断业务流程
MySQL 表结构怎么设计才支持高效地理查询
光有 lng 和 lat 两个 decimal 字段是不够的。不建空间索引,ST_Distance_Sphere 就没法加速,数据一过万就明显变慢。
必须做三件事:
- 加一个
POINT类型字段,比如location,用POINT(lng, lat)初始化 - 给该字段建空间索引:
SPATIAL INDEX idx_location (location) - 插入/更新时同步维护:
Db::table('shops')->insert(['location' => Db::raw("POINT($lng, $lat)")])
注意:POINT 字段不能为 NULL,且 MySQL 的 ST_* 函数对 NULL 输入会返回 NULL,查不到结果也不报错——容易误判为“没数据”。
为什么用 Db::raw('ACOS(...)') 写 Haversine 总是不准
核心原因就两个:参数顺序反了、没转弧度。Haversine 公式里 COS 和 SIN 的输入必须是弧度,而数据库里的经纬度是角度。
典型翻车写法:
ACOS(SIN(lat1) * SIN(lat2) + COS(lat1) * COS(lat2) * COS(lng2 - lng1)) * 6371
正确写法(MySQL):
ACOS( SIN(RADIANS(lat1)) * SIN(RADIANS(lat2)) + COS(RADIANS(lat1)) * COS(RADIANS(lat2)) * COS(RADIANS(lng2 - lng1)) ) * 6371
更稳妥的做法是直接用 ST_Distance_Sphere,它内部已处理好球面模型和单位转换,还支持 WGS84 坐标系校准——这点在跨省查询时误差能差几百米。
实际项目里,地理查询的瓶颈往往不在公式本身,而在坐标来源是否一致(比如前端用 GCJ-02,后端用 WGS-84)、缓存是否穿透、边界条件是否覆盖(海洋、国界、极地)。这些比选哪个函数重要得多。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











