MySQL 选择特定的列比选择慢 *
Posted
技术标签:
【中文标题】MySQL 选择特定的列比选择慢 *【英文标题】:MySQL select specific cols slower than select * 【发布时间】:2011-02-27 21:50:26 【问题描述】:我的 mysql 不是很强大,所以请原谅任何菜鸟的错误。短版:
SELECT locId,count,avg FROM destAgg_geo 明显比 SELECT * from destAgg_geo 慢
prtt.destAgg 是一个以 dst_ip (PRIMARY) 为键的表
mysql> describe prtt.destAgg;
+---------+------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------+------------------+------+-----+---------+-------+
| dst_ip | int(10) unsigned | NO | PRI | 0 | |
| total | float unsigned | YES | | NULL | |
| avg | float unsigned | YES | | NULL | |
| sqtotal | float unsigned | YES | | NULL | |
| sqavg | float unsigned | YES | | NULL | |
| count | int(10) unsigned | YES | | NULL | |
+---------+------------------+------+-----+---------+-------+
geoip.blocks 是一个以 startIpNum 和 endIpNum (PRIMARY) 为键的表
mysql> describe geoip.blocks;
+------------+------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------+------------------+------+-----+---------+-------+
| startIpNum | int(10) unsigned | NO | MUL | NULL | |
| endIpNum | int(10) unsigned | NO | | NULL | |
| locId | int(10) unsigned | NO | | NULL | |
+------------+------------------+------+-----+---------+-------+
destAgg_geo 是一个视图:
CREATE VIEW destAgg_geo AS SELECT * FROM destAgg JOIN geoip.blocks
ON destAgg.dst_ip BETWEEN geoip.blocks.startIpNum AND geoip.blocks.endIpNum;
这里是select *的优化方案:
mysql> explain select * from destAgg_geo;
+----+-------------+---------+------+---------------+------+---------+------+---------+------------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------+------+---------------+------+---------+------+---------+------------------------------------------------+
| 1 | SIMPLE | blocks | ALL | start_end | NULL | NULL | NULL | 3486646 | |
| 1 | SIMPLE | destAgg | ALL | PRIMARY | NULL | NULL | NULL | 101893 | Range checked for each record (index map: 0x1) |
+----+-------------+---------+------+---------------+------+---------+------+---------+------------------------------------------------+
以下是针对特定列的 select 的优化计划:
mysql> explain select locId,count,avg from destAgg_geo;
+----+-------------+---------+------+---------------+------+---------+------+---------+------------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------+------+---------------+------+---------+------+---------+------------------------------------------------+
| 1 | SIMPLE | destAgg | ALL | PRIMARY | NULL | NULL | NULL | 101893 | |
| 1 | SIMPLE | blocks | ALL | start_end | NULL | NULL | NULL | 3486646 | Range checked for each record (index map: 0x1) |
+----+-------------+---------+------+---------------+------+---------+------+---------+------------------------------------------------+
以下是 destAgg 中每一列的优化计划,以及 geoip.blocks 中的 locId 列:
mysql> explain select dst_ip,total,avg,sqtotal,sqavg,count,locId from destAgg_geo;
+----+-------------+---------+------+---------------+------+---------+------+---------+------------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------+------+---------------+------+---------+------+---------+------------------------------------------------+
| 1 | SIMPLE | blocks | ALL | start_end | NULL | NULL | NULL | 3486646 | |
| 1 | SIMPLE | destAgg | ALL | PRIMARY | NULL | NULL | NULL | 101893 | Range checked for each record (index map: 0x1) |
+----+-------------+---------+------+---------------+------+---------+------+---------+------------------------------------------------+
删除除 dst_ip 之外的任何列,范围检查会翻转为块:
mysql> explain select dst_ip,avg,sqtotal,sqavg,count,locId from destAgg_geo;
+----+-------------+---------+------+---------------+------+---------+------+---------+------------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------+------+---------------+------+---------+------+---------+------------------------------------------------+
| 1 | SIMPLE | destAgg | ALL | PRIMARY | NULL | NULL | NULL | 101893 | |
| 1 | SIMPLE | blocks | ALL | start_end | NULL | NULL | NULL | 3486646 | Range checked for each record (index map: 0x1) |
+----+-------------+---------+------+---------------+------+---------+------+---------+------------------------------------------------+
然后慢得多。这是怎么回事?
(是的,我可以从那里使用 * 查询结果和过程,但我想知道发生了什么以及为什么)
编辑 -- 对 VIEW 查询的解释:
mysql> explain SELECT * FROM destAgg JOIN geoip.blocks ON destAgg.dst_ip BETWEEN geoip.blocks.startIpNum AND geoip.blocks.endIpNum;
+----+-------------+---------+------+---------------+------+---------+------+---------+------------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------+------+---------------+------+---------+------+---------+------------------------------------------------+
| 1 | SIMPLE | blocks | ALL | start_end | NULL | NULL | NULL | 3486646 | |
| 1 | SIMPLE | destAgg | ALL | PRIMARY | NULL | NULL | NULL | 101893 | Range checked for each record (index map: 0x1) |
+----+-------------+---------+------+---------------+------+---------+------+---------+------------------------------------------------+
【问题讨论】:
这是一个视图:CREATE VIEW destAgg_geo AS SELECT * FROM destAgg JOIN geoip.blocks ON destAgg.dst_ip BETWEEN geoip.blocks.startIpNum AND geoip.blocks.endIpNum; 请在构成视图的查询中显示 EXPLAIN 结果。 添加到原问题 【参考方案1】:如果您对两个查询都运行 EXPLAIN PLAN,MySQL 会告诉您。
带有列的第一个查询不包含任何键列,所以我猜它必须执行 TABLE SCAN。
带有“SELECT *”的第二个查询包含主键,因此它可以使用索引。
【讨论】:
EXPLAIN PLAN 给我一个错误 1064(保留关键字):mysql> EXPLAIN PLAN select * from destAgg_geo; ERROR 1064 (42000):您的 SQL 语法有错误;检查与您的 MySQL 服务器版本相对应的手册,以在第 1 行 mysql> EXPLAIN PLAN (select * from destAgg_geo) 的“select * from destAgg_geo”附近使用正确的语法; ERROR 1064 (42000):您的 SQL 语法有错误;检查与您的 MySQL 服务器版本相对应的手册,以在第 1 行的“(从 destAgg_geo 中选择 *)”附近使用正确的语法 EXPLAIN PLAN 是 Oracle 语法。 MySQL 只是使用 EXPLAIN 后跟查询。【参考方案2】:范围过滤器是最后应用的,所以问题是查询优化器在一种情况下选择先连接较大的表,而在另一种情况下选择先连接较小的表。也许对优化器有更多了解的人可以告诉我们为什么它以不同的顺序加入每个表。
我认为这里的真正目标应该是尝试让 JOIN 使用索引,因此连接的顺序并不重要。
【讨论】:
【参考方案3】:我会尝试在 locId、count、avg 上放置一个综合索引,看看这是否不会提高速度。
【讨论】:
以上是关于MySQL 选择特定的列比选择慢 *的主要内容,如果未能解决你的问题,请参考以下文章