我有一个MySQL数据库。我将家庭存储在数据库中,只对数据库执行1次查询, 但我需要超级快速执行此查询 ,这将返回广场内的所有家庭box geo latitude&经度。
SELECT * FROM homes
WHERE geolat BETWEEN ??? AND ???
AND geolng BETWEEN ??? AND ???
如何以最佳方式存储我的地理数据,以便我可以执行此查询,以便最快地在地理位置框中显示所有住宅?
基本上:
如果它有帮助,我在下面包含我的数据库表模式:
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
请注意,下面没有人真正回答过这个问题。我真的很期待得到任何帮助。提前谢谢。
答案 0 :(得分:13)
关于MySQL地理定位性能here的论文很好。
编辑非常确定这是使用固定半径。此外,我并非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);
SELECT dest.*,
3956 * 2 * ASIN(
SQRT(
POWER(
SIN(
(user_lat - dest.lat) * pi() / 180 / 2
), 2
) + COS(
user_lat * pi() / 180
) * COS(
dest.lat * pi() / 180
) * POWER(
SIN(
(user_lon - dest.lon) * pi() / 180 / 2
), 2
)
)
) as distance
FROM dest
WHERE
dest.lon between min_lon and max_lon AND
dest.lat between min_lat and max_lat
HAVING distance < radius
ORDER BY distance
LIMIT 10
答案 1 :(得分:4)
答案 2 :(得分:2)
如果您确实需要提高性能,可以为数据定义边界框,并在插入时将预计算边界框映射到对象,稍后再使用它们进行查询。
如果结果集相当小,您仍然可以在应用程序逻辑中进行精度校正(比数据库更容易按比例缩放),同时能够提供准确的结果。
看看Bret Slatkin的geobox.py,其中包含有关该方法的精彩文档。
如果您打算在可预见的未来进行更复杂的查询,我仍然建议检查PostgreSQL和PostGIS与MySQL相比。
答案 3 :(得分:1)
您使用的索引确实是B树索引,并支持查询中的BETWEEN
关键字。这意味着优化器能够使用您的索引来查找“框”中的房屋。然而,它并不意味着它将始终使用指数。如果指定的范围包含太多“命中”,则不会使用索引。
答案 4 :(得分:1)
这是我用过的一些成功的技巧是创建圆整区域。也就是说,如果您的位置位于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]
使用匹配的舍入标签拉出所有位置,然后,一旦将它们从数据库中取出,就可以进行距离计算以确定要使用的位置。
答案 5 :(得分:0)
这看起来非常快。我唯一关心的是它会使用一个索引来获取纬度3英里范围内的所有值,然后过滤那些经度3英里范围内的值。如果我理解底层系统是如何工作的,那么每个表只能使用一个INDEX,因此lat或long上的索引都是毫无价值的。
如果您有大量数据,可能加快速度,为每1x1英里的正方形提供一个唯一的逻辑ID,然后对SELECT进行额外的限制(area =“23234 / 34234“OR area =”23235/34234“或...”对于您点周围的所有方块,然后强制数据库使用该索引而不是lat和long。那么你只会过滤更少平方英里的数据。
答案 6 :(得分:0)
院?你可能甚至不会有一万个。只需使用内容索引,如STRTree。
答案 7 :(得分:0)
坚持你目前的做法,你应该做出一个改变, 你应该有一个复合索引,而不是单独索引geolat和geolong:
KEY `geolat_geolng` (`geolat`, `geolng`),
目前,您的查询只会利用两个索引中的一个。
答案 8 :(得分:0)
一个非常好的选择是 MongoDB 及其Geospatial Indexing。
答案 9 :(得分:0)
您可以考虑创建一个单独的表'GeoLocations',其主键为('geolat','geolng'),并且如果该特定地理位置恰好有一个主页,则该列包含home_id。这应允许优化器搜索一系列地理位置,这些地理位置将在磁盘上排序以获取home_id列表。然后,您可以使用“homes”表执行连接,以查找有关这些home_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
答案 10 :(得分:0)
由于MySQL 5.7 mysql可以使用像ST_Distance_Sphere()和ST_Contains()那样的geoindex来提高性能。