计算大型数据集的地理点之间的距离

Posted

技术标签:

【中文标题】计算大型数据集的地理点之间的距离【英文标题】:Calculating distance between Geo points for large datasets 【发布时间】:2017-07-31 15:28:39 【问题描述】:

我正在构建一个在线 Symfony 应用程序,作为开发过程的一部分,我的任务是根据与登录用户的距离对大量数据库记录进行排序;所述用户可以随意扩大搜索范围,最大可达整个世界的大小。

在任何时候我都可以访问登录用户的 GPS 坐标,并且在数据库表中我保存了各个兴趣点的纬度和经度。

目前,POI 表中只有 400 条记录,但由于每次访问时我必须提取的数据量很大,查询时间已经超过一秒。向这样的工作负载添加 400 个三角函数很快就会使这样的执行时间超出可接受的范围。

因此,我需要一种既快速又准确的方法来计算这些距离;

我已经阅读了多篇建议使用 Haversine 公式的文章,但我发现这对于我的需求来说太慢了,甚至像 this 这样的大量文章也无济于事;

考虑到我很快就会接触到数千个 POI,同时有来自世界各地的数千名用户同时登录,我该如何解决(并希望解决)这样的问题?

我正在使用 php 7.0、Symfony 3.2 和 Doctrine; pdo 连接到 mysql 服务器,使用 innoDB 作为数据库引擎 我的客户重视准确性而不是速度,但不能等待超过 5 秒 查询结果是分页的,因此无法将排序委托给客户端 数据库和php服务器共享同一个(可怕的)资源池,这个池要与其他应用程序共享

顺便说一句,某些 POI 可能会在某个日期后过期

【问题讨论】:

我不确定Haversine 是您的性能问题,我们在生产中使用它,并且我们每分钟进行超过 150k 次搜索(它确实使用了多个 PHP 工作者),但是您实际上是否标记了如何计算需要多长时间。 这是一个 PHP 代码中的问题***.com/questions/14750275/haversine-formula-with-php 在MySQL中有一种计算方式,但是不准确,而且速度较慢。 @ArtisticPhoenix 我只对 symfony 的分析器进行了基准测试,当我开始考虑距离计算和排序时,已经很长的查询时间增加了大约 5%; 我不使用交响乐只编写我自己的东西,但你可以借用我的基准类github.com/ArtisticPhoenix/Evo/blob/master/Evo/Benchmark.php你只需做$mark = Benchmark->getInstance()->mark()然后在你做Benchmark->getInstance()->format($mark) 【参考方案1】:

你让我添加它,所以我会添加它。

您确定性能影响来自 Haversine 吗?在我的工作中,我们已经在生产中成功使用了这个公式的 PHP 实现大约 2 年,并且我们进行了大量搜索(高峰时间每分钟大约 15 万次)。

我无法详细了解我的工作,但我可以说我们使用 sphinx、mongoDB、mysql 和 RabbitMq 的组合。

在任何情况下,sphinx 和 mysql 都存在距离计算执行不佳的问题,在 100 英里的距离上损失了大约 2 英里的准确度。(这就是我们使用它的原因)

您可以做的一件事是对运行 Haversine 公式所需的时间进行基准测试,当您遇到性能问题时,良好的基准测试是第一步。

虽然我不是交响乐用户,但我确实有一个专门为此而设的课程。它是我在业余时间构建的更大框架的一部分(进化)。你可以在这里上课

https://github.com/ArtisticPhoenix/Evo/blob/master/Evo/Benchmark.php

使用非常简单

$mark = Benchmark::getInstance()->mark();

... code to time ...

echo Benchmark::getInstance()->format($mark);

并且会输出类似

的东西
10 milliseconds
5 minutes 3 milliseconds
ect..

它的设计让你可以使用多个marks

$mark = Benchmark::getInstance()->mark();

... code to time ...

$mark1 = Benchmark::getInstance()->mark();

 ... more code to time ...

echo "TotalTime: ".Benchmark::getInstance()->format($mark);
echo "MethodTime: ".Benchmark::getInstance()->format($mark1);

etc..

当您调用mark() 时,它基本上只记录microtime(true)(true 为浮点数)并返回标识符$mark,然后如果您使用标识符调用mark($mark),它将从当前的microtime(true) 中减去它.调用 format($mark) 只会使其更具“人类”可读性。

希望对你有帮助!

【讨论】:

以上是关于计算大型数据集的地理点之间的距离的主要内容,如果未能解决你的问题,请参考以下文章

KNN算法介绍

Highcharts 跳过大型数据集的共享工具提示点

创建大型数据集的邻域列表/紧固

计算大型数据集的python树高度

计算多点之间地理空间距离的最有效方法?

在 R 中处理大型数据集