京东外卖,探索「距离最近」排序背后的秘密

关注我的公众号:【编程朝花夕拾】,可获取首发内容。

1、引言

京东外卖于‌2025年2月11日正式开放,标志着京东正式进军外卖行业。浏览页面时,在筛选项里面看到了「距离最近」的筛选项,点了一下就按照商户和当前位置正序排列了。

这个功能其实其他外卖类、旅游类、地图类的APP中经常看到。今天我们一起聊一下背后的技术方案。

2、背后的技术

地理位置(Geolocation)距离的不再是简单的距离比较,而是实时的经纬度的计算。因为你在不同的地方,商家离你的位置都是变化的。

Geolocation的简称是GEO。地球经纬度,将地球看做一个球体。将A、B两点的作为一个剖面,就会得到一个圆形,A、B两点的之间的距离不再试一条直线,而是一条弧线。两点的距离就是这个弧长。

常用的计算方式,离不开数学的三角函数:

  • 球面余弦定理:适用于远距离计算,但近距离可能有舍入误差
  • Vincenty公式:基于椭球模型,精度更高但计算复杂
  • Haversine公式:平衡精度与复杂度的最佳选择,适用于导航、地图服务等常见场景

了解一下晦涩难懂的Haversine公式:

看着复杂,其实按照参数代入即可。当然这个不是今天的目的。

Java作为一门面向对象的语言,可以不用关注具体的实现,只要有现成的工具,调用即可。

3、问题难点

了解了经纬度的计算,无论是我们自己编码、套用公式还是使用一些在线工具根据两点计算出距离,这些都不能满足类似「京东外卖」这样的排序。

因为排序功能是针对全量的数据。如果单纯的计算出距离,不说海量万级数据都有可能将内存榨干,更不用说影响用户体验了。

难点来了:如何通过经纬度实时计算给万级数据排序呢?

4、Mysql 5.7.6+

Mysql 是大部分程序员都会接触的数据库,用来存储数据。Mysql5.7.6 版本 开始正式支持基于地理坐标系(如WGS84)的经纬度计算功能,关键改进是引入了 ST_Distance_Sphere 函数,用于计算球面距离(即地球表面的实际距离)。

数据准备:

上海(POINT(121.4737, 31.2304))、北京(POINT(116.4074, 39.9042))

mysql 复制代码
SELECT ST_Distance_Sphere(
    POINT(116.4074, 39.9042),  -- 北京
    POINT(121.4737, 31.2304)    -- 上海
) AS distance_meters;
-- 结果约为 1,066,000 米(约1066公里)

距离正序排列的使用:

mysql 复制代码
-- 单位米
SELECT
    id,
    latitude,
    longitude,
    ST_DISTANCE_SPHERE(
        POINT(target_longitude, target_latitude),
        POINT(longitude, latitude)
    ) AS distance
FROM table_name 
ORDER BY distance ASC;

5、Redis GEO

Redis中的GEO数据类型是Redis3.2.0开始有的。

实战用到的函数:GEORADIUS

shell 复制代码
# 根据当前经纬度获取方圆范围内的成员
GEORADIUS key longitude latitude radius M | KM | FT | MI [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count [ANY]] [ASC | DESC] [STORE key | STOREDIST key]

参数含义:

  • WITHCOORD 获取经纬度
  • WITHDIST 获取距离
  • WITHHASH 获取成员Hash
  • COUNT 限制罗列的成员数量
  • ASE | DESC 排序
  • STORE key 将返回的地理位置信息保存到一个key里面
  • STOREDIST key 将返回的目标距离保存到一个key里面

实战:

假设当前的地点位置:上海(POINT(121.4737, 31.2304)

shell 复制代码
# 查询cities这个key中距离上海(POINT(121.4737, 31.2304) 50公里以内的元素,并正序排列
GEORADIUS cities 121.4737 31.2304 50 KM WITHDIST ASC

像了解关于Redis GEO更多知识可以查看以往文章:

mp.weixin.qq.com/s/MbQXBvN2k...

6、小结

上面介绍两种最常用且简单的方法,你还知道其他方法么?评论区留言讨论。

方法再多,只不过使我们使用的工具或者手段。日常中你可能平时常见的一个小功能,背后可能都有极其复杂的技术支撑。比如我们进场使用的地图,已经接入北斗导航,背后的技术更是不言而喻。

相关推荐
♡喜欢做梦5 分钟前
【MySQL】联合查询
数据库·mysql
向哆哆33 分钟前
Spring Boot快速开发:从零开始搭建一个企业级应用
java·spring boot·后端
[email protected]2 小时前
ASP.NET Core 中实现 Markdown 渲染中间件
后端·中间件·asp.net·.netcore
gadiaola6 小时前
MySQL从入门到精通(三):MySQL数据类型、SQL语言—DDL
数据库·sql·mysql·database
eternal__day7 小时前
Spring Boot 实现验证码生成与校验:从零开始构建安全登录系统
java·spring boot·后端·安全·java-ee·学习方法
海天胜景8 小时前
HTTP Error 500.31 - Failed to load ASP.NET Core runtime
后端·asp.net
海天胜景8 小时前
Asp.Net Core IIS发布后PUT、DELETE请求错误405
数据库·后端·asp.net
小刘|9 小时前
Redis 中简单动态字符串(SDS)的深入解析
数据库·redis·bootstrap
源码云商10 小时前
Spring Boot + Vue 实现在线视频教育平台
vue.js·spring boot·后端
万能程序员-传康Kk10 小时前
中国邮政物流管理系统(Django+mysql)
python·mysql·django