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

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

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

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

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

相关推荐
e***193520 小时前
MySQL-触发器(TRIGGER)
android·数据库·mysql
Aiden1212120 小时前
Mysql主从复制与读写分离
数据库·mysql
databook20 小时前
Manim v0.19.1 发布啦!三大新特性让动画制作更丝滑
后端·python·动效
IT_陈寒20 小时前
Vite 5个隐藏功能大揭秘:90%的开发者都不知道这些提速技巧!
前端·人工智能·后端
m***l11520 小时前
【Spring Boot】Spring AOP中的环绕通知
spring boot·后端·spring
v***56520 小时前
常见的 Spring 项目目录结构
java·后端·spring
j***121520 小时前
MySQL如何执行.sql 文件:详细教学指南
数据库·mysql
叫致寒吧20 小时前
Mysql
数据库·mysql
b***666120 小时前
Spring Framework 中文官方文档
java·后端·spring
8***235520 小时前
SQL Server2022版+SSMS安装教程(保姆级)
后端·python·flask