对约 225 万行的单表进行选择查询的优化技术?

Posted

技术标签:

【中文标题】对约 225 万行的单表进行选择查询的优化技术?【英文标题】:Optimization techniques for select query on single table with ~2.25M rows? 【发布时间】:2013-08-14 16:23:07 【问题描述】:

我有一个在 InnoDB 引擎上运行的名为 squaresmysql 表,它大约有 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_latref_coord_long 分别保存一个点的一组十进制度的纬度和经度坐标。

这是一个只读表。不会添加额外的行,唯一需要对其运行的查询如下:

SELECT * FROM `squares` WHERE 
  `ref_coord_lat` BETWEEN :southLat AND :northLat AND 
  `ref_coord_long` BETWEEN :westLong AND :eastLong

...冒号后面的值是 php PDO 占位符。本质上,此查询的目标是获取表中当前位于由查询中的 4 个坐标限制的 Google 地图窗口的视口中的所有坐标点。

我已限制使用 Google Maps API 运行此查询的缩放级别,以便可以获取的最大行数为 ~5600。随着缩放级别的增加,最终的获取总数会显着降低。

直接在 PHPMyAdmin 中运行这样的示例查询需要 1.40-1.45 秒。这太长了。我已经在ref_coord_latref_coord_long 上运行标准索引,这使查询时间从约5 秒减少,但这对于最终用户期望及时响应的地图来说仍然太大了。

我的问题很简单:如何进一步优化此表/查询以提高获取结果的速度?

【问题讨论】:

您是否尝试过按 long 和 lat 创建索引? “我已经在 ref_coord_lat 和 ref_coord_long 上运行标准索引,这将查询时间从大约 5 秒缩短” 我的意思是按 (long, lat) 索引 - 即两列上的索引 数据是否适合内存?为您的 Web 服务器购买更多 RAM 并使用 SQL 在启动时播种静态缓存。如果您需要闪电般的速度,请摆脱每个请求的 SQL 网络之旅。 检查 InnoDB 变量 innodb_buffer_pool 的大小。如果它是 8 兆字节,这意味着您正在运行一个默认配置,这使得 MySQL 像蜗牛一样运行。增加缓冲池,你会让它运行得更快。 【参考方案1】:

(lat, long) 上创建复合索引应该会有很大帮助。

但是,正确的解决方案是查看MySQL spatial extensions。空间支持是专门为处理二维数据和针对此类数据的查询而创建的。如果您创建适当的空间索引,您的典型查询性能应该很容易超过 (lat, long) 上的复合索引的性能。

【讨论】:

复合索引最终没有帮助(好吧,我在减少索引大小方面获得了相当的性能)。但是您对 MySQL 空间扩展的看法是正确的。我已经切换(请参阅我自己的答案)并且我已经看到查询速度急剧增加。【参考方案2】:

你的结构看起来还不错。 2,25M 行并不多。您的行很小,并且您所做的比较仅针对双精度值。它应该更快。

尝试在您的表上运行ANALYZEOPTIMIZECHECKREPAIR 命令,以确保您的索引构造正确。

完成此操作后,您应该尝试在系统中进行更深入的调查。 是什么减慢了查询速度?可以是:

磁盘 I/O 内存限制(尝试调整你的 my.cnf,查看优秀的http://www.mysqlperformanceblog.com/) CPU(似乎不太可能) 网络问题

使用监控来获取有关您的 sql 缓存、内存使用情况等的数据。 它将帮助您诊断问题。

祝你的项目好运。

【讨论】:

我同意,查询和表是完美的,加快速度的唯一方法是查看系统工具,尤其是缓存... 感谢您的帮助。 MySQL 通过ANALYZEOPTIMIZE 告诉我我的结构没问题。我最终切换到 MySQL 空间扩展(请参阅我的帖子正文中的自我回答)。【参考方案3】:

这里的内容最初是由 OP (Antilogical) 作为对问题的编辑而编写的。我将问题的答案部分移至此处,并将其设为社区 wiki。 @Antilogical,如果您想发表自己的答案以获得声誉,请给我留言。

嗯,我修好了。方法如下:

我设法将查询时间从最初的 5 秒缩短到 0.6-0.7 毫秒。我偶然发现了这个问题,“如何为单个查询进一步优化这个 MySQL 表”here。这导致我将我的表从 InnoDB 切换到 MyISAM 并使用地理空间抽象来表示我的坐标点。

首先,我从 InnoDB 切换到 MyISAM,它更适合 MySQL 空间扩展。

ALTER TABLE `squares` ENGINE=MyISAM;

然后,我创建了一个名为coordinate 的地理空间列,其中包含一个点对象(它只是ref_coord_latref_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 空间扩展时,我现在可以通过使用具有函数 MBRContains() 的最小边界矩形来检查我的新 coordinate 列,该函数是 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`);

速度提高了多个数量级:

~5s - 查询初始squares 表的时间(InnoDB,无索引) 1.40-1.45s - 为ref_coord_latref_coord_long 添加两个索引 0.9s (900ms) - 然后我发现我的 SELECT 查询中的约束坐标有十几个小数位。我在我的 javascript 代码中将它们四舍五入为 6(与我的表存储坐标的小数位数相同)。这提供了不错的速度提升。 0.5s (500ms) - 根据@N.B. 下面的评论,我将inno_db_buffer_pool 的大小从 16M 增加到 256M。 45-50ms - 切换到 MyISAM 引擎,添加坐标点列并添加空间索引 0.6-0.7ms - 我改变了我的查询,而不是选择 * 列,而是选择所有 but 我的新 coordinate 列。

数据库优化?完毕。

【讨论】:

【参考方案4】:

虽然不是很优雅,但拆分为多个表(例如每 30 度纬度一个)通常会有所帮助。您的查询很明显哪些表包含所需的点。

另外,使用EXPLAIN 来调查问题。

【讨论】:

另外,与其拆分表,不如对它进行分区。正如您所说,EXPLAIN 输出将更清楚地说明性能问题。

以上是关于对约 225 万行的单表进行选择查询的优化技术?的主要内容,如果未能解决你的问题,请参考以下文章

sql 10多万行的数据 求助

优化 100 万行的查询

Oracle 查询技巧与优化 单表查询与排序

Mysql的单表查询

MySQL的单表查询

Oracle的单表查询语句