Mysql 数据库:查询地理位置数据的最佳性能方式?

Mysql 数据库:查询地理位置数据的最佳性能方式?,mysql,database,database-design,performance,Mysql,Database,Database Design,Performance,我有一个MySQL数据库。我将房屋存储在数据库中,并对数据库执行一次查询,但我需要以超快的速度执行此查询,即返回一个方形框中的所有房屋地理纬度和经度 SELECT * FROM homes WHERE geolat BETWEEN ??? AND ??? AND geolng BETWEEN ??? AND ??? 对于我来说,存储地理数据的最佳方式是什么,以便我能够最快地执行在地理位置框中显示所有家庭的查询 基本上: 我是否使用最好的SQL语句以最快的速度执行此查询 是否存在其他方法,甚

我有一个MySQL数据库。我将房屋存储在数据库中,并对数据库执行一次查询,但我需要以超快的速度执行此查询,即返回一个方形框中的所有房屋地理纬度和经度

SELECT * FROM homes 
WHERE geolat BETWEEN ??? AND ???
AND geolng BETWEEN ??? AND ???
对于我来说,存储地理数据的最佳方式是什么,以便我能够最快地执行在地理位置框中显示所有家庭的查询

基本上:

  • 我是否使用最好的SQL语句以最快的速度执行此查询
  • 是否存在其他方法,甚至可能不使用数据库,让我以最快的方式查询装箱地理位置范围内的房屋结果
如果有帮助,我已将数据库表模式包括在下面:

CREATE TABLE IF NOT EXISTS `homes` (
  `home_id` int(10) unsigned NOT NULL auto_increment,
  `address` varchar(128) collate utf8_unicode_ci NOT NULL,
  `city` varchar(64) collate utf8_unicode_ci NOT NULL,
  `state` varchar(2) collate utf8_unicode_ci NOT NULL,
  `zip` mediumint(8) unsigned NOT NULL,
  `price` mediumint(8) unsigned NOT NULL,
  `sqft` smallint(5) unsigned NOT NULL,
  `year_built` smallint(5) unsigned NOT NULL,
  `geolat` decimal(10,6) default NULL,
  `geolng` decimal(10,6) default NULL,
  PRIMARY KEY  (`home_id`),
  KEY `geolat` (`geolat`),
  KEY `geolng` (`geolng`),
) ENGINE=InnoDB  ;
更新


我了解地球曲率的空间因素,但我最感兴趣的是以最快的速度返回地理数据。除非这些空间数据库包以某种方式更快地返回数据,否则请不要推荐空间扩展。谢谢

更新2


请注意,下面没有人真正回答这个问题。我真的很期待能得到任何帮助。提前感谢。

如果您确实需要提高性能,您可以为数据定义边界框,并在插入时将预计算边界框映射到对象,然后将其用于查询

如果结果集相当小,您仍然可以在应用程序逻辑中进行精度校正(比数据库更易于横向扩展),同时能够提供准确的结果

看一看Bret Slatkin的,其中包含了关于该方法的大量文档


如果您打算在可预见的将来进行更复杂的查询,我仍然建议您查看PostgreSQL,并与MySQL进行比较。

您使用的索引实际上是B树索引,并且在查询中支持
BETWEEN
关键字。这意味着优化器能够使用您的索引在您的“盒子”中找到家。然而,这并不意味着它将始终使用指数。如果指定的范围包含太多的“点击”,则不会使用索引。

这看起来相当快。我唯一担心的是,它将使用索引获取纬度3英里范围内的所有值,然后过滤经度3英里范围内的值。如果我理解底层系统是如何工作的,那么每个表只能使用一个索引,所以lat或long上的索引都是无用的


如果您有大量数据,它可能会加快速度,为每个1x1平方英里提供一个唯一的逻辑ID,然后对点周围所有正方形的选择(area=“23234/34234”或area=“23235/34234”或…)进行额外限制,然后强制数据库使用该索引,而不是lat和long。然后,您只需过滤更少的平方英里数据

我也有同样的问题,写了一篇由三部分组成的博客文章。这比地理索引快


家?你可能连一万个都没有。只需使用内存中的索引,如。

有一篇关于MySQL地理位置性能的好文章

编辑非常确定这是使用固定半径。此外,我不能100%确定计算距离的算法是最先进的(即,它将“钻”穿地球)

重要的是,该算法很便宜,可以对行数进行大致限制,以便进行适当的距离搜索


该算法通过在源点周围的正方形中提取候选点,然后计算距离(以英里为单位)

预先计算此值,或者按照源代码的建议使用存储过程:

# Pseudo code
# user_lon and user_lat are the source longitude and latitude
# radius is the radius where you want to search
lon_distance = radius / abs(cos(radians(user_lat))*69);
min_lon = user_lon - lon_distance;
max_lon = user_lon + lon_distance;
min_lat = user_lat - (radius / 69);
max_lat = user_lat + (radius / 69);
选择dest.*,
3956*2*ASIN(
SQRT(
权力(
罪(
(用户坐标-目标坐标)*pi()/180/2
), 2
)+COS(
用户_lat*pi()/180
)*COS(
dest.lat*pi()/180
)*权力(
罪(
(用户长度-目标长度)*pi()/180/2
), 2
)
)
)作为距离
从目的地
哪里
最小长度和最大长度之间的距离,以及
最小纬度和最大纬度之间的目的纬度
距离小于半径的
按距离排序
限制10

坚持你目前的方法,你应该做一个改变, 您不应该单独索引geolat和geolong,而应该有一个复合索引:

KEY `geolat_geolng` (`geolat`, `geolng`),

目前,您的查询将只利用两个索引中的一个。

一个很好的替代方法是使用its的MongoDB

我使用了一个技巧,成功地创建了舍入区域。也就是说,如果你有一个位于36.12345,-120.54321的位置,并且你想将其与半英里(近似)网格框内的其他位置分组,你可以将其区域称为36.12x-120.54,并且具有相同舍入区域的所有其他位置将落在同一个框中

显然,这并不能为你提供一个干净的半径,也就是说,如果你所观察的位置更靠近一条边而不是另一条边。然而,通过这种设置,计算围绕主位置方框的八个方框就足够容易了。也就是说:

[36.13x-120.55][36.13x-120.54][36.13x-120.53]
[36.12x-120.55][36.12x-120.54][36.12x-120.53]
[36.11x-120.55][36.11x-120.54][36.11x-120.53]

将所有的位置都用匹配的圆形标签标记,然后,一旦你把它们从数据库中取出,你就可以进行距离计算来确定要使用哪种。

你可以考虑创建一个单独的表“地理位置”,它有一个主键(‘GeOLAT’,GEOLNG’)。并且有一个列,如果某个特定地理位置碰巧有一个家,该列将保存home_id。这应该允许优化器搜索一系列地理位置,这些地理位置将在磁盘上排序,以获得主ID列表。然后,您可以执行与“homes”表的联接,以查找有关这些home\u id的信息

CREATE TABLE IF NOT EXISTS `GeoLocations` (
`geolat` decimal(10,6) NOT NULL,
`geolng` decimal(10,6) NOT NULL,
`home_id` int(10) NULL
PRIMARY KEY  (`geolat`,`geolng`)
);

SELECT GL.home_id
FROM GeoLocations GL
INNER JOIN Homes H
 ON GL.home_id = H.home_id
WHERE GL.geolat between X and Y
 and GL.geolng between X and Y

由于MySQL 5.7,MySQL可以使用geoindex,如ST_Distance_Sphere()和ST_Contains(),以提高性能。

UTM公司
CREATE TABLE IF NOT EXISTS `GeoLocations` (
`geolat` decimal(10,6) NOT NULL,
`geolng` decimal(10,6) NOT NULL,
`home_id` int(10) NULL
PRIMARY KEY  (`geolat`,`geolng`)
);

SELECT GL.home_id
FROM GeoLocations GL
INNER JOIN Homes H
 ON GL.home_id = H.home_id
WHERE GL.geolat between X and Y
 and GL.geolng between X and Y