我有一个名为squares
的InnoDB引擎上运行的MySQL表,它有大约2,250,000行,具有以下表结构:
`squares` (
`square_id` int(7) unsigned NOT NULL,
`ref_coord_lat` double(8,6) NOT NULL,
`ref_coord_long` double(9,6) NOT NULL,
PRIMARY KEY (`square_id`),
KEY `ref_coord_lat` (`ref_coord_lat`),
KEY `ref_coord_long` (`ref_coord_long`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
第一列square_id
包含一个简单的递增值,范围为0 - 2.25M,而ref_coord_lat
& ref_coord_long
分别为一个点保存一组十进制度的纬度和经度坐标。
这是一个只读表。不会添加其他行,并且需要针对它运行的唯一查询如下:
SELECT * FROM `squares` WHERE
`ref_coord_lat` BETWEEN :southLat AND :northLat AND
`ref_coord_long` BETWEEN :westLong AND :eastLong
...其中冒号后面的值是PHP PDO占位符。本质上,此查询的目标是获取表格中当前位于Google地图窗口视口中的所有坐标点,该窗口由查询中的4个坐标限定。
我限制了使用Google Maps API运行此查询的缩放级别,以便可以获取的最大行数 ~5600 。随着缩放级别的增加,合成的提取总量显着减少。
直接在PHPMyAdmin中运行这样的示例查询需要1.40-1.45秒。这太长了。我已经在ref_coord_lat
和ref_coord_long
上运行标准索引,这使得查询时间从大约5秒开始下降,但对于最终用户期望及时响应的地图来说,这仍然太大了。
我的问题很简单:如何进一步优化此表/查询以提高获取结果的速度?
答案 0 :(得分:3)
你的结构似乎很好。 2,25M行并没有那么多。你的行很小,你所做的比较只是双值。它应该更快。
尝试在您的表格上运行ANALYZE
,OPTIMIZE
,CHECK
,REPAIR
命令,以确保正确构建索引。
完成此操作后,您应该尝试深入研究系统。 什么在减慢查询速度?它可以是:
使用监控来获取有关sql缓存,内存使用情况等的数据。 它可以帮助您诊断问题。
祝你的项目好运。
答案 1 :(得分:3)
在(lat, long)
上创建复合索引应该会有很多帮助。
但是,正确的解决方案是查看MySQL spatial extensions。专门创建空间支持来处理针对此类数据的二维数据和查询。如果创建适当的空间索引,则典型的查询性能应该轻松超过(lat, long)
上的复合索引的性能。
答案 2 :(得分:2)
此处的内容最初由OP(Antilogical)编写,作为问题的编辑。我将问题的答案部分移到了这里,并将其作为社区维基。 @Antilogical,如果你想发表你自己的答案以获得声誉,请点评我评论。
我设法将查询时间从最初的5秒减少到 0.6-0.7毫秒。我偶然发现了这个问题,'如何针对单个查询进一步优化此MySQL表'here。这导致我将我的表从InnoDB切换到MyISAM并使用地理空间抽象来表示我的坐标点。
首先,我从InnoDB切换到MyISAM,它更适合MySQL空间扩展。
ALTER TABLE `squares` ENGINE=MyISAM;
然后,我创建了一个名为coordinate
的地理空间列,其中包含一个点对象(它只是ref_coord_lat
和ref_coord_long
的串联:
UPDATE `squares` SET `coordinate` = GeomFromText(CONCAT('POINT(', `ref_coord_lat`,' ', `ref_coord_long`, ')'));
我向coordinate
添加了一个空间索引 - 这大大提高了查询性能。最初在不使用地理空间扩展时,我从数据库中选择字段的查询是:
SELECT * FROM `squares` WHERE `ref_coord_lat` BETWEEN *somecoordinate* AND *somecoordinate* AND `ref_coord_long` BETWEEN *somecoordinate* and *somecoordinate*
这个查询本质上做的是通过设置每个轴的两个限制/条件(纬度和经度)来模仿边界框。请注意,*
当前表示我的数据库的所有三个字段,我还没有创建coordinate
。当我切换到使用MySQL空间扩展时,我现在可以通过使用函数coordinate
的最小边界矩形来检查我的新MBRContains()
列,该函数是MySQL地理空间扩展集的一部分。
SELECT * FROM `squares` WHERE MBRContains(GeomFromText(POLYGON((*my four bounds now go here in lat,lon pairs*))), `coordinate`);
注意我仍在选择*
的所有字段?你不需要这样做。 coordinate
列仅仅作为查看值的索引,所以我现在通过下面的查询选择除该列之外的所有内容,这比直接上面的那个提供了显着的速度增加。
SELECT `square_id`, `ref_coord_lat`, `ref_coord_long` FROM `squares` WHERE MBRContains(GeomFromText(POLYGON((*my four bounds now go here in lat,lon pairs*))), `coordinate`);
速度增加了几个数量级:
squares
表的时间(InnoDB,没有索引)ref_coord_lat
&添加两个指数ref_coord_long
inno_db_buffer_pool
的大小从16M增加到256M。 coordinate
列,而不是选择*列。 数据库优化?完成。
答案 3 :(得分:0)
虽然不是很优雅,但分成多个表格(比如说每30度纬度一个)通常会有所帮助。您的查询很明显哪些表包含所需的点。
另外,使用EXPLAIN来调查问题。