0

I'm building an online Symfony application, and as part of the developement process i've been tasked with sorting an amount of database records based on the distance from the logged user; said user can widen the search radius at will, up to the size of the whole world.

At any moment i have access to the GPS coordinates of the logged user, and on a database table i saved latitude and longitude of the various points of interest.

Currently, there are only 400 records in the POIs' table, but due to the amount of data i must extract whenever i access it, the query time is already slightly over a second. Adding 400 trigonometric functions to such workload will soon bring such execution time beyond acceptability.

I thus need a method that's both fast and accurate to calculate such distances;

I've read multiple articles suggesting the Haversine formula, but i found that to be too slow for my needs and even an extensive article like this couldn't be of any help;

Considering that i could soon reach thousands of POIs with thousands of users logged at the same time from all over the world, how could i approach (and hopefully solve) such problem?

I'm using PHP 7.0, Symfony 3.2, and Doctrine; pdo to interface to a Mysql server, with innoDB as the database engine
My customer values accuracy over speed, but can't stand to wait more than 5 seconds
The query results are paged, thus delegating the sorting to the client is impossible
Both the database and the php server share the same (terrible) resource pool, and such pool is to be shared with other applications

On a sidenote, some of the POIs may expire after a certain date

4

1 回答 1

0

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

你确定性能打击来自 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..

它基本上只是microtime(true)在您调用时记录 (true is as float)mark()并返回一个标识符$mark,然后如果您mark($mark)使用该标识符调用它,它将从当前的microtime(true). 调用format($mark)只是使其更具“人类”可读性。

希望对你有帮助!

于 2017-07-31T16:58:53.987 回答