【发布时间】:2019-05-30 12:08:44
【问题描述】:
我的查询很慢。
SELECT
posts.*
FROM
posts
INNER JOIN categories ON posts.category_id = categories.id
AND categories.main = 1
AND(categories.private_category = 0
OR categories.private_category IS NULL)
WHERE
posts.id NOT IN('')
AND posts.deleted = 0
AND posts.hidden = 0
AND posts.total_points >= - 5
ORDER BY
posts.id DESC
LIMIT 10;
所以在我解释之后:
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
|----|-------------|-------------|------------|------|------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------|---------|-------------------------------|-------|----------|----------------------------------------------|
| 1 | SIMPLE | categories | | ALL | PRIMARY,index_categories_on_private_category | | | | 12 | 10.00 | Using where; Using temporary; Using filesort |
| 1 | SIMPLE | posts | | ref | PRIMARY,index_posts_on_category_id,index_posts_on_deleted_and_hidden_and_user_id_and_created_at,index_posts_deleted,index_posts_hidden,index_posts_total_points | index_posts_on_category_id | 5 | mydb.categories.id | 37516 | 12.50 | Using index condition; Using where |
我在categories.main上添加了一个索引:
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
|----|-------------|-------------|--------|------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------|---------|-----------------------------------|-------|-------------|
| 1 | SIMPLE | posts | range | PRIMARY,index_posts_on_category_id,index_posts_on_deleted_and_hidden_and_user_id_and_created_at,index_posts_deleted,index_posts_hidden,index_posts_total_points | PRIMARY | 4 | | 37516 | Using where |
| 1 | SIMPLE | categories | eq_ref | PRIMARY,index_categories_on_private_category,index_categories_on_main | PRIMARY | 4 | mydb.posts.category_id | 12 | Using where |
它表明它不使用索引(?)。查询仍然很慢,我想优化它。查询有什么问题?
编辑
posts 表的创建方式如下:
CREATE TABLE `posts` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`user_id` int(11) DEFAULT NULL,
`category_id` int(11) DEFAULT NULL,
`title` varchar(1000) NOT NULL,
`content` text,
`total_points` int(11) DEFAULT '0',
`deleted` tinyint(1) DEFAULT '0',
`hidden` tinyint(1) DEFAULT '0',
`created_at` datetime DEFAULT NULL,
`updated_at` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `index_posts_on_category_id` (`category_id`),
KEY `index_posts_created_at` (`created_at`),
KEY `index_posts_on_deleted_and_hidden_and_user_id_and_created_at` (`deleted`,`hidden`,`user_id`,`created_at`),
KEY `index_posts_deleted` (`deleted`),
KEY `index_posts_hidden` (`hidden`),
KEY `index_posts_total_points` (`total_points`),
KEY `index_posts_user_id` (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=304063 DEFAULT CHARSET=utf8
类别表的创建方式如下:
CREATE TABLE `categories` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`title` varchar(100) NOT NULL,
`main` tinyint(1) DEFAULT '0',
`created_at` datetime DEFAULT NULL,
`updated_at` datetime DEFAULT NULL,
`hidden` tinyint(1) DEFAULT '0',
`private_category` tinyint(1) DEFAULT '0',
PRIMARY KEY (`id`),
KEY `index_categories_on_private_category` (`private_category`),
KEY `index_categories_on_main` (`main`)
) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8
【问题讨论】:
-
主要问题是查询规划器认为最好在
posts表之前访问categories以减少记录,但它需要存储一个临时表Using temporary;,它可以是内存或基于磁盘...还需要`使用文件排序`文件排序是一个奇怪的名字,但这表明快速排序算法在数据上运行 -
但是要求性能的答案应该包括
SHOW CREATE TABLE table输出以及问题中涉及的每个表。 -
解释简单的顺序..如果您运行
SELECT STRAIGHT_JOIN posts.*,您会注意到顺序会发生变化,并且很可能Using temporary; Using filesort也会消失,因为索引正确.. 警告STRAIGHT_JOIN不是一个解决方案,因为有一天它可以正常工作,而另一天则不行/执行 annyway.. -
"显示它没有使用索引(?)。查询仍然很慢,我想优化它。查询有什么问题?"建立索引并不意味着 MySQL 必须使用它。优化器是基于成本的,第二个解释表明使用两个表中的 PRIMARY KEY 是获取数据的最便宜的方法。
-
另外我相信升级到 MySQL 8.0 也会有所帮助,因为优化器倾向于尽可能避免
Using temporary; Using filesort,但可能会扫描表/索引文件中的更多记录..@987654337当涉及大型表/结果集时,@组合可能是 MySQL 性能的真正杀手。
标签: mysql