mysql查询优化程序使用分区的子查询比使用索引的左连接具有更好的性能
在Ubuntu12.04 LTS上使用mysql版本5.6.14-enterprise-commercial-advanced-log时,我在从这些表查询数据时遇到以下行为:mysql查询优化程序使用分区的子查询比使用索引的左连接具有更好的性能,mysql,sql,indexing,partitioning,myisam,Mysql,Sql,Indexing,Partitioning,Myisam,在Ubuntu12.04 LTS上使用mysql版本5.6.14-enterprise-commercial-advanced-log时,我在从这些表查询数据时遇到以下行为: CREATE TABLE `a` ( `id` varchar(32) DEFAULT NULL, `request_time` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00', ) ENGINE=MyISAM DEF
CREATE TABLE `a` (
`id` varchar(32) DEFAULT NULL,
`request_time` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
) ENGINE=MyISAM DEFAULT CHARSET=latin1
!50100 PARTITION BY RANGE (UNIX_TIMESTAMP(request_time))
(PARTITION p15062116 VALUES LESS THAN (1434895200) ENGINE = MyISAM,
PARTITION p15062117 VALUES LESS THAN (1434898800) ENGINE = MyISAM,
PARTITION p15062118 VALUES LESS THAN (1434902400) ENGINE = MyISAM,
...
PARTITION rest VALUES LESS THAN MAXVALUE ENGINE = MyISAM)
CREATE TABLE `b` (
`id` varchar(50) NOT NULL,
`start_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`item` int(11) DEFAULT NULL,
`item2` int(11) DEFAULT NULL,
PRIMARY KEY (`id`,`start_time`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8
!50100 PARTITION BY RANGE (UNIX_TIMESTAMP(start_time))
(PARTITION p15062516 VALUES LESS THAN (1435240800) ENGINE = MyISAM,
PARTITION p15062517 VALUES LESS THAN (1435244400) ENGINE = MyISAM
....
PARTITION rest VALUES LESS THAN MAXVALUE ENGINE = MyISAM)
使用此查询将导致1秒的运行时间:
SELECT SQL_NO_CACHE request_time, item
FROM a left join
(select *
from b
where start_time between '2015-06-28 10:00:00' and '2015-06-28 11:00:00'
) c using(id)
where request_time between '2015-06-28 10:00:00' and '2015-06-28 11:00:00'
解释输出:
+----+-------------+------------+------+---------------+-------------+---------+------+--------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+------------+------+---------------+-------------+---------+------+--------+-------------+
| 1 | PRIMARY | a | ALL | NULL | NULL | NULL | NULL | 336972 | Using where |
| 1 | PRIMARY | <derived2> | ref | <auto_key0> | <auto_key0> | 152 | func | 10 | Using where |
| 2 | DERIVED | b | ALL | NULL | NULL | NULL | NULL | 39508 | Using where |
+----+-------------+------------+------+---------------+-------------+---------+------+--------+-------------+
3 rows in set (0.00 sec)
mysql> explain partitions SELECT SQL_NO_CACHE request_time, item FROM a left join (select * from b where start_time between '2015-06-28 10:00:00' and '2015-06-28 11:00:00') b using(id) where request_time between '2015-06-28 10:00:
+----+-------------+------------+---------------------+------+---------------+-------------+---------+------+--------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+------------+---------------------+------+---------------+-------------+---------+------+--------+-------------+
| 1 | PRIMARY | a | p15062810,p15062811 | ALL | NULL | NULL | NULL | NULL | 336972 | Using where |
| 1 | PRIMARY | <derived2> | NULL | ref | <auto_key0> | <auto_key0> | 152 | func | 10 | Using where |
| 2 | DERIVED | b | p15062810,p15062811 | ALL | NULL | NULL | NULL | NULL | 39508 | Using where |
+----+-------------+------------+---------------------+------+---------------+-------------+---------+------+--------+-------------+
+----+-------------+-----------+------+---------------+---------+---------+------+--------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-----------+------+---------------+---------+---------+------+--------+--------------------------+
| 1 | SIMPLE | a | ALL | NULL | NULL | NULL | NULL | 336972 | Using where |
| 1 | SIMPLE | b | ref | PRIMARY | PRIMARY | 152 | func | 395 | Using where; Using index |
+----+-------------+-----------+------+---------------+---------+---------+------+--------+--------------------------+
mysql> explain partitions SELECT SQL_NO_CACHE request_time, item FROM a left join b using(id) where request_time between '2015-06-28 10:00:00' and '2015-06-28 11:00:00' and start_time between '2015-06-28 10:00:00' and '2015-06-28 11:
+----+-------------+-----------+---------------------+------+---------------+---------+---------+------+--------+--------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-----------+---------------------+------+---------------+---------+---------+------+--------+--------------------------+
| 1 | SIMPLE | a | p15062810,p15062811 | ALL | NULL | NULL | NULL | NULL | 336972 | Using where |
| 1 | SIMPLE | b | p15062810,p15062811 | ref | PRIMARY | PRIMARY | 152 | func | 395 | Using where; Using index |
+----+-------------+-----------+---------------------+------+---------------+---------+---------+------+--------+--------------------------+
解释输出:
+----+-------------+------------+------+---------------+-------------+---------+------+--------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+------------+------+---------------+-------------+---------+------+--------+-------------+
| 1 | PRIMARY | a | ALL | NULL | NULL | NULL | NULL | 336972 | Using where |
| 1 | PRIMARY | <derived2> | ref | <auto_key0> | <auto_key0> | 152 | func | 10 | Using where |
| 2 | DERIVED | b | ALL | NULL | NULL | NULL | NULL | 39508 | Using where |
+----+-------------+------------+------+---------------+-------------+---------+------+--------+-------------+
3 rows in set (0.00 sec)
mysql> explain partitions SELECT SQL_NO_CACHE request_time, item FROM a left join (select * from b where start_time between '2015-06-28 10:00:00' and '2015-06-28 11:00:00') b using(id) where request_time between '2015-06-28 10:00:
+----+-------------+------------+---------------------+------+---------------+-------------+---------+------+--------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+------------+---------------------+------+---------------+-------------+---------+------+--------+-------------+
| 1 | PRIMARY | a | p15062810,p15062811 | ALL | NULL | NULL | NULL | NULL | 336972 | Using where |
| 1 | PRIMARY | <derived2> | NULL | ref | <auto_key0> | <auto_key0> | 152 | func | 10 | Using where |
| 2 | DERIVED | b | p15062810,p15062811 | ALL | NULL | NULL | NULL | NULL | 39508 | Using where |
+----+-------------+------------+---------------------+------+---------------+-------------+---------+------+--------+-------------+
+----+-------------+-----------+------+---------------+---------+---------+------+--------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-----------+------+---------------+---------+---------+------+--------+--------------------------+
| 1 | SIMPLE | a | ALL | NULL | NULL | NULL | NULL | 336972 | Using where |
| 1 | SIMPLE | b | ref | PRIMARY | PRIMARY | 152 | func | 395 | Using where; Using index |
+----+-------------+-----------+------+---------------+---------+---------+------+--------+--------------------------+
mysql> explain partitions SELECT SQL_NO_CACHE request_time, item FROM a left join b using(id) where request_time between '2015-06-28 10:00:00' and '2015-06-28 11:00:00' and start_time between '2015-06-28 10:00:00' and '2015-06-28 11:
+----+-------------+-----------+---------------------+------+---------------+---------+---------+------+--------+--------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-----------+---------------------+------+---------------+---------+---------+------+--------+--------------------------+
| 1 | SIMPLE | a | p15062810,p15062811 | ALL | NULL | NULL | NULL | NULL | 336972 | Using where |
| 1 | SIMPLE | b | p15062810,p15062811 | ref | PRIMARY | PRIMARY | 152 | func | 395 | Using where; Using index |
+----+-------------+-----------+---------------------+------+---------------+---------+---------+------+--------+--------------------------+
根据id索引和1小时分区的使用情况,我期望第二次查询得到类似或更好的结果。
两个表各有1000000条记录
你能解释一下为什么第一个查询比第二个查询效率高很多吗
我们是否可以重构第一个查询,使其成为视图或可重用查询,而不是为每个联接重新生成子查询
谢谢我猜SQL引擎很难确定第二个查询的分区。您可以尝试将其编写为:
SELECT SQL_NO_CACHE count(*)
FROM a left join
b
using(id)
where request_time between '2015-06-28 10:00:00' and '2015-06-28 11:00:00' and
start_time between '2015-06-28 10:00:00' and '2015-06-28 11:00:00'
UNION ALL
SELECT SQL_NO_CACHE count(*)
FROM a left join
b
using (id)
WHERE request_time between '2015-06-28 10:00:00' and '2015-06-28 11:00:00' and
start time is null ;
我知道这会返回两行。如果这有助于引擎找到正确的分区,那么将这些值添加到一起就很容易了。我猜SQL引擎很难确定第二个查询的分区。您可以尝试将其编写为:
SELECT SQL_NO_CACHE count(*)
FROM a left join
b
using(id)
where request_time between '2015-06-28 10:00:00' and '2015-06-28 11:00:00' and
start_time between '2015-06-28 10:00:00' and '2015-06-28 11:00:00'
UNION ALL
SELECT SQL_NO_CACHE count(*)
FROM a left join
b
using (id)
WHERE request_time between '2015-06-28 10:00:00' and '2015-06-28 11:00:00' and
start time is null ;
我知道这会返回两行。如果这有助于引擎找到正确的分区,那么将这些值添加在一起就很容易了。您确定优化器可以为这个表达式
UNIX\u TIMESTAMP(request\u time)
执行分区消除吗?顺便说一句,两个查询返回完全不同的结果。Q1与没有任何联接的简单计数(*)相同,而Q2与内部联接相同。是的,它可以基于此进行分区消除,在其他任何情况下都有效。你对这个问题的看法是正确的,这是错误的。我会解决的。你正在使用MyISAM存储进行“优化”。这太傻了。您的所有查询和测试只测量操作系统安排硬盘读取的效果。MyISAM是过时的引擎,至少使用InnoDB并在交换存储后开始优化。您可以更好地对其进行微调,以获得更快的结果。1m记录等于零,使用InnoDB,您至少可以控制RAM中的数据量,这样您就可以避免所有的磁盘搜索。Myisam是必须的,这是目前产品的限制。在a
中是否真的没有索引?您确定优化器可以为这个表达式UNIX\u时间戳进行分区消除吗(request_time)
?顺便说一句,两个查询返回完全不同的结果。Q1与没有任何联接的简单计数(*)相同,而Q2与内部联接相同。是的,它可以在此基础上进行分区消除,它在其他任何情况下都有效。您对查询的看法是正确的,它是错误的。我会修复它。您正在“优化”使用MyISAM存储。这太傻了。你所有的查询和测试都只衡量操作系统安排硬盘读取的好坏。MyISAM是过时的引擎,至少使用InnoDB并在交换存储后开始优化。你可以更好地对其进行微调以获得更快的结果。1m记录等于零,使用InnoDB你至少可以控制如何进行大量数据放入RAM,因此您可以避免一起进行磁盘查找。Myisam是必须的,这是当前产品的限制。在a
中是否真的没有索引?实际上是计数(*)这只是一个例子,真实的实时场景涉及两个表中的实际数据。@yonialon…问题是将其分成两个子查询是否会加快查询速度。无论操作是什么,将数据组合在一起都非常简单。实际上,计数(*)这只是一个例子,真实的实时场景涉及两个表中的实际数据。@yonialon…问题是将其分解为两个子查询是否会加快查询速度。无论操作是什么,将数据合并在一起都非常简单。