计算大型数据集的地理点之间的距离
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)
只会使其更具“人类”可读性。
希望对你有帮助!
【讨论】:
以上是关于计算大型数据集的地理点之间的距离的主要内容,如果未能解决你的问题,请参考以下文章