当前位置: 首页 > 面试题库 >

PHP / MySQL中的地理搜索(距离)(性能)

丁成弘
2023-03-14
问题内容

我有一个MySQL表(MyISAM),其中包含我根据与另一对经纬度对之间的距离(大圆公式)从中选择的约20万经纬度对条目。(例如,在50.281852,2.504883附近的10公里半径内的所有条目)

我的问题是此查询大约需要0.28秒。仅针对这200k条目运行(每天仍在增加)。而0.28秒。通常情况下会很好,此查询会经常运行,因为它会增强我的Web应用程序的主要功能,而且通常是较大查询的一部分。

有什么办法可以加快速度吗?显然,MySQL每次必须遍历所有200k条目,并为每个条目执行大圈公式。我在stackoverflow上阅读了一些有关geohashing,R-Trees之类的内容,但我不认为这是我想要的方式。部分原因是我从来都不是数学的忠实拥护者,但主要是因为我认为这个问题已经由比我聪明的人在图书馆/扩展名等中解决了。已进行了广泛的测试,并定期进行更新。

MySQL似乎具有空间扩展性,但是没有提供距离功能。我是否应该查看另一个数据库来放置这些坐标对?PostgreSQL似乎具有相当成熟的Spatial扩展。你知道吗
还是PostgreSQL是否仅使用大圆公式来获取特定区域内的所有条目?

是否有专门的独立产品或mysql扩展已经可以满足我的需求?

还是有可能我可以使用PHP库进行计算?使用APC,我可以轻松地将经纬度对放入内存(200k条目需要约5MB),然后在PHP内部运行查询。但是,这种方法的问题是,然后我将有一个SELECT
.. FROM .. WHERE
id在(id1,id2,..)中的MySQL查询,因为所有结果最多可达几千个。MySQL如何处理此类查询?然后(由于这是一个数字运算任务)在PHP中执行此操作是否足够快?

还有其他想法我应该/不应该做什么?

对于completeneses,这里是示例查询,其中删除了所有不相关的部分(如我所说,通常这是我联接多个表的更大查询的一部分):

SELECT id, 6371 * acos( sin( radians( 52.4042924 ) ) * sin( radians( lat ) ) + cos( radians( 50.281852 ) ) * cos( radians( lat ) ) * cos( radians( 2.504883 ) - radians( lon ) ) ) AS dst
FROM geoloc
HAVING dst <10
ORDER BY dst ASC

谢谢!


问题答案:

计算边界框以选择SQL查询的WHERE子句中的行的子集,以便仅对该行子集执行昂贵的距离计算,而不是对表中的全部200k记录执行。本文在Movable
Type
(带有PHP代码示例)中描述了该方法。然后,您可以针对该子集在查询中包含Haversine计算,以计算实际距离,并在该点处将其包含在HAVING子句中。

这是帮助您提高性能的边界框,因为这意味着您仅对一小部分数据进行了昂贵的距离计算。实际上,这是与Patrick所建议的方法相同的方法,但是Movable
Type链接对方法进行了详尽的解释,以及可用于构建边界框和SQL查询的PHP代码。

编辑

如果您认为Haversine不够准确,那么还有Vincenty公式。

//  Vincenty formula to calculate great circle distance between 2 locations expressed as Lat/Long in KM

function VincentyDistance($lat1,$lat2,$lon1,$lon2){
    $a = 6378137 - 21 * sin($lat1);
    $b = 6356752.3142;
    $f = 1/298.257223563;

    $p1_lat = $lat1/57.29577951;
    $p2_lat = $lat2/57.29577951;
    $p1_lon = $lon1/57.29577951;
    $p2_lon = $lon2/57.29577951;

    $L = $p2_lon - $p1_lon;

    $U1 = atan((1-$f) * tan($p1_lat));
    $U2 = atan((1-$f) * tan($p2_lat));

    $sinU1 = sin($U1);
    $cosU1 = cos($U1);
    $sinU2 = sin($U2);
    $cosU2 = cos($U2);

    $lambda = $L;
    $lambdaP = 2*M_PI;
    $iterLimit = 20;

    while(abs($lambda-$lambdaP) > 1e-12 && $iterLimit>0) {
        $sinLambda = sin($lambda);
        $cosLambda = cos($lambda);
        $sinSigma = sqrt(($cosU2*$sinLambda) * ($cosU2*$sinLambda) + ($cosU1*$sinU2-$sinU1*$cosU2*$cosLambda) * ($cosU1*$sinU2-$sinU1*$cosU2*$cosLambda));

        //if ($sinSigma==0){return 0;}  // co-incident points
        $cosSigma = $sinU1*$sinU2 + $cosU1*$cosU2*$cosLambda;
        $sigma = atan2($sinSigma, $cosSigma);
        $alpha = asin($cosU1 * $cosU2 * $sinLambda / $sinSigma);
        $cosSqAlpha = cos($alpha) * cos($alpha);
        $cos2SigmaM = $cosSigma - 2*$sinU1*$sinU2/$cosSqAlpha;
        $C = $f/16*$cosSqAlpha*(4+$f*(4-3*$cosSqAlpha));
        $lambdaP = $lambda;
        $lambda = $L + (1-$C) * $f * sin($alpha) * ($sigma + $C*$sinSigma*($cos2SigmaM+$C*$cosSigma*(-1+2*$cos2SigmaM*$cos2SigmaM)));
    }

    $uSq = $cosSqAlpha*($a*$a-$b*$b)/($b*$b);
    $A = 1 + $uSq/16384*(4096+$uSq*(-768+$uSq*(320-175*$uSq)));
    $B = $uSq/1024 * (256+$uSq*(-128+$uSq*(74-47*$uSq)));

    $deltaSigma = $B*$sinSigma*($cos2SigmaM+$B/4*($cosSigma*(-1+2*$cos2SigmaM*$cos2SigmaM)- $B/6*$cos2SigmaM*(-3+4*$sinSigma*$sinSigma)*(-3+4*$cos2SigmaM*$cos2SigmaM)));

    $s = $b*$A*($sigma-$deltaSigma);
    return $s/1000;
}


echo VincentyDistance($lat1,$lat2,$lon1,$lon2);


 类似资料:
  • 本文向大家介绍PHP实现搜索地理位置及计算两点地理位置间距离的实例,包括了PHP实现搜索地理位置及计算两点地理位置间距离的实例的使用技巧和注意事项,需要的朋友参考一下 地理位置搜寻 LBS,存储每个地点的经纬度坐标,搜寻附近的地点,建立地理位置索引可提高查询效率。 mongodb地理位置索引,2d和2dsphere,对应平面和球面。 1.创建lbs集合存放地点坐标 2.创建地理位置索引 3.查询附

  • 我正在实现一个程序,能够接收10个最近的对象。作为数据库,我使用弹性搜索,我的模型看起来像这样。 我创建了一个这样的索引 我会插入像这样的新文档 该模型的所有值都已设置,并且可以正常工作。 但是现在我想得到10公里范围内最近的10个。我这样质疑这个 但是它不返回任何文档,但是如果我将距离值设置为10000km,它将返回文档。 我在模型中使用的数据是:LAT:47.4595248 LON:9.638

  • 我正在尝试按地理距离对具有嵌套地理点映射的索引进行排序。 这是我的简化映射: 这里,每个组织可以有几个地点点。 文档: 这是我的查询(PHP数组): 预期结果 : 我希望按geo_point排序资源(检查每个位置,然后检查某个位置是否靠近给定的纬度/经度)

  • 问题内容: 我正在努力创建一个搜索多个单词的搜索。我的第一次尝试没有任何结果,结果如下: 我还尝试了以下方法,这些方法产生了结果,但是正如您可以想象的那样,我输入的每个单词都得到了重复的结果: 我对如何进行此操作非常迷惑,不胜感激。 编辑: 问题答案: 我已经在同一个主题(搜索关键字)上工作了一段时间,这是我的工作方式: -现在这就是我尝试使用FULLTEXT搜索运行它的样子:但是您应该将表类型设

  • 问题内容: 我需要测量以字符串形式提供名称的两个地方之间的物理距离。由于有时名称的书写方式略有不同,因此我一直在寻找一个可以帮助我测量差异的库,然后将其与纬度和经度结合起来以选择正确的匹配项。首选语言:Java或PHP。 有什么建议? 问题答案: 看看Levenshtein距离。这是一种测量两个字符串彼此之间有多不同的方法。 希望我能正确理解你的问题;在与“经度”相同的句子中使用“距离”可能会造成

  • 本文向大家介绍php两点地理坐标距离的计算方法,包括了php两点地理坐标距离的计算方法的使用技巧和注意事项,需要的朋友参考一下 本文实例为大家分享了php计算两点地理坐标距离的具体代码,供大家参考,具体内容如下 功能:根据圆周率和地球半径系数与两点坐标的经纬度,计算两点之间的球面距离。 获取两点坐标距离: 以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持呐喊教程。