我正在尝试优化一些查询并得到一些令人费解的结果(可能源于我对MySQL内部工作的有限理解)。
令人困惑的事情(至少对我而言)是当我试图剖析完整的查询来优化它时,我发现内部选择查询(子查询)在其上运行得更多,更慢拥有;我认为更简单的查询会运行得更快。贝娄是查询和我的结果:
SELECT r.id, r.serve_url, r.title, r.category_id, GROUP_CONCAT(hr.server_id) AS server_id
FROM hosted_resources hr
LEFT JOIN resources AS r ON (hr.resource_id = r.id)
WHERE hr.resource_id = (
select id from resources
where resource_type_id = 1
and category_id = 1
and id < 311
order by date_added desc
limit 1
)
GROUP BY r.id, r.serve_url, r.title, r.category_id;
EXPLAIN查询的结果:
+----+-------------+-----------+-------+-------------------------------------------------------------------------------+----------------------------------------------+---------+-------+------+-----------------------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-----------+-------+-------------------------------------------------------------------------------+----------------------------------------------+---------+-------+------+-----------------------------------------------------------+
| 1 | PRIMARY | hr | ref | hosted_resources_resource_id_resource_id_idx,hosted_resources_resource_id_idx | hosted_resources_resource_id_resource_id_idx | 4 | const | 2 | Using where; Using index; Using temporary; Using filesort |
| 1 | PRIMARY | r | const | PRIMARY | PRIMARY | 4 | const | 1 | |
| 2 | SUBQUERY | resources | ref | PRIMARY,type_idx,category_idx,type_category_idx,type_category_date_idx | type_category_date_idx | 8 | | 87 | Using where; Using index |
+----+-------------+-----------+-------+-------------------------------------------------------------------------------+----------------------------------------------+---------+-------+------+-----------------------------------------------------------+
基准测试结果:
Concurrency Level: 5000
Time taken for tests: 9.396 seconds
Complete requests: 100000
Failed requests: 0
Write errors: 0
Non-2xx responses: 100000
Total transferred: 31900000 bytes
HTML transferred: 16900000 bytes
Requests per second: 10642.78 [#/sec] (mean)
Time per request: 469.802 [ms] (mean)
Time per request: 0.094 [ms] (mean, across all concurrent requests)
Transfer rate: 3315.47 [Kbytes/sec] received
select id, serve_url, title, category_id from resources
where resource_type_id = 1
and category_id = 1
and id < 311
order by date_added desc
limit 1
EXPLAIN查询的结果:
+----+-------------+-----------+------+------------------------------------------------------------------------+------------------------+---------+-------------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-----------+------+------------------------------------------------------------------------+------------------------+---------+-------------+------+-------------+
| 1 | SIMPLE | resources | ref | PRIMARY,type_idx,category_idx,type_category_idx,type_category_date_idx | type_category_date_idx | 8 | const,const | 87 | Using where |
+----+-------------+-----------+------+------------------------------------------------------------------------+------------------------+---------+-------------+------+-------------+
基准测试结果:
Concurrency Level: 5000
Time taken for tests: 42.181 seconds
Complete requests: 100000
Failed requests: 0
Write errors: 0
Total transferred: 41800000 bytes
HTML transferred: 27100000 bytes
Requests per second: 2370.75 [#/sec] (mean)
Time per request: 2109.040 [ms] (mean)
Time per request: 0.422 [ms] (mean, across all concurrent requests)
Transfer rate: 967.75 [Kbytes/sec] received
资源表:
CREATE TABLE `resources` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`date_added` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT
`resource_type_id` int(11) NOT NULL COMMENT,
`resource_status_id` int(11) NOT NULL COMMENT
`is_hosted` bit(1) NOT NULL COMMENT
`category_id` int(11) NOT NULL COMMENT
`serve_url` varchar(255) DEFAULT NULL COMMENT
`title` varchar(255) DEFAULT NULL COMMENT
`parent_resource_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `serve_url_UNIQUE` (`serve_url`),
KEY `type_idx` (`resource_type_id`),
KEY `status_idx` (`resource_status_id`),
KEY `category_idx` (`category_id`),
KEY `resources_parent_resource_id_idx` (`parent_resource_id`),
KEY `type_category_idx` (`resource_type_id`,`category_id`),
KEY `date_added_idx` (`date_added`),
KEY `type_category_date_idx` (`resource_type_id`,`category_id`,`date_added`),
CONSTRAINT `resources_category_id` FOREIGN KEY (`category_id`) REFERENCES `categories` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT `resources_parent_resource_id` FOREIGN KEY (`parent_resource_id`) REFERENCES `resources` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `resources_resource_status_id` FOREIGN KEY (`resource_status_id`) REFERENCES `resource_statuses` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT `resources_resource_type_id` FOREIGN KEY (`resource_type_id`) REFERENCES `resource_types` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=598 DEFAULT CHARSET=latin1;
任何强硬都会非常值得赞赏。 凯特
答案 0 :(得分:0)
尝试删除密钥:
KEY `type_category_date_idx` (`resource_type_id`,`category_id`,`date_added`)
此键包含上述其他键。据我所知,mysql可以同时使用2个不同的密钥。如果这三个频繁用于查找此表,则删除密钥可能不那么简单。
答案 1 :(得分:0)
你的查询不具有可比性,当你自己运行子查询时它有其他列,我相信这就是出现差异的地方,你可以在EXPLAIN中看到主要查询的子查询的额外内容: / p>
Using where; Using index
当您使用其他列自行运行子查询时,它只显示:
Using where
原因是您的非聚簇索引仅存储索引列和主键,因此您的索引:
KEY `type_category_date_idx` (`resource_type_id`,`category_id`,`date_added`)
有足够的信息来满足您的整个子查询:
SELECT ID
FROM Resources
WHERE resource_type_id = 1
AND category_id = 1
AND id < 311
ORDER BY date_added DESC
因此无需参考表格数据。当您向选择列表添加更多列时,索引不再包含所有列 满足查询所需的信息,查询需要使用id列表执行书签查找表数据 从索引中,找到相关列中的数据。
这是My SQL Docs对Using Index
所说的内容:
仅使用索引树中的信息从表中检索列信息,而无需进行额外的搜索 阅读实际的行。查询使用时可以使用此策略 只有属于单个索引的列。
如果Extra列也显示Using where,则表示索引用于执行键值的查找。没有使用的地方, 优化器可能正在读取索引以避免读取数据行而不是 用它来查找。例如,如果索引是覆盖索引 对于查询,优化器可以在不使用它的情况下扫描它 查找。
您应该看到,如果仅使用SELECT ID FROM...
运行子查询,那么您将获得与主查询中相同的计划,并且比使用其他列运行它更快的执行时间。
修改强>
在实际优化您的查询方面,我认为您可以将LEFT JOIN
更改为INNER JOIN
,因为根据resources
您知道WHERE
中的ID必须存在条款。除此之外,我没有看到很多imrpovement的范围,虽然我相信MySQL将比where子句中的子查询更好地处理JOIN,所以以下可能表现更好:
SELECT r.id, r.serve_url, r.title, r.category_id, GROUP_CONCAT(hr.server_id) AS server_id
FROM resources r
INNER JOIN
( SELECT ID
FROM Resources
WHERE resource_type_id = 1
AND category_id = 1
AND id < 311
ORDER BY date_added DESC
LIMIT 1
) MaxR
ON MaxR.ID = r.ID
INNER JOIN hosted_resources hr
ON hr.resource_id = r.id
GROUP BY r.id, r.serve_url, r.title, r.category_id;