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

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

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、小结

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

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

相关推荐
想用offer打牌4 小时前
MCP (Model Context Protocol) 技术理解 - 第二篇
后端·aigc·mcp
陌上丨5 小时前
Redis的Key和Value的设计原则有哪些?
数据库·redis·缓存
KYGALYX5 小时前
服务异步通信
开发语言·后端·微服务·ruby
掘了5 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
ccecw6 小时前
Mysql ONLY_FULL_GROUP_BY模式详解、group by非查询字段报错
数据库·mysql
JH30736 小时前
达梦数据库与MySQL的核心差异解析:从特性到实践
数据库·mysql
爬山算法6 小时前
Hibernate(90)如何在故障注入测试中使用Hibernate?
java·后端·hibernate
Moment6 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
Cobyte7 小时前
AI全栈实战:使用 Python+LangChain+Vue3 构建一个 LLM 聊天应用
前端·后端·aigc
程序员侠客行8 小时前
Mybatis连接池实现及池化模式
java·后端·架构·mybatis