我正在尝试优化此查询:
SELECT `posts`.* FROM `posts` INNER JOIN `posts_tags` ON `posts`.id = `posts_tags`.post_id WHERE (((`posts_tags`.tag_id = 1))) ORDER BY posts.created_at DESC;
表的大小是 38k 行,31k 和 mysql 使用“文件排序”,所以它变得很慢。我尝试使用不同的索引,没有运气。
创建表`帖子`( `id` int(11) NOT NULL auto_increment, `created_at` 日期时间默认 NULL, 主键(`id`), KEY `index_posts_on_created_at` (`created_at`), KEY `for_tags`(`trashed`、`published`、`clan_private`、`created_at`) ) 引擎=InnoDB AUTO_INCREMENT=44390 默认字符集=utf8 排序=utf8_unicode_ci 创建表`posts_tags`( `id` int(11) NOT NULL auto_increment, `post_id` int(11) 默认 NULL, `tag_id` int(11) 默认 NULL, `created_at` 日期时间默认 NULL, `updated_at` 日期时间默认 NULL, 主键(`id`), KEY `index_posts_tags_on_post_id_and_tag_id` (`post_id`,`tag_id`) ) 引擎=InnoDB AUTO_INCREMENT=63175 默认字符集=utf8
+----+-------------+------------+--------+-------- ------------------+----------------------------+---- -----+----------+--------+-------------- ---------------------------------------------+ | 编号 | 选择类型 | 表| 类型 | 可能的键 | 关键 | key_len | 参考 | 行 | 额外 | +----+-------------+------------+--------+-------- ------------------+----------------------------+---- -----+----------+--------+-------------- ---------------------------------------------+ | 1 | 简单 | 帖子标签 | 索引 | index_post_id_and_tag_id | index_post_id_and_tag_id | 10 | 空 | 24159 | 使用哪里;使用索引;使用临时的;使用文件排序 | | 1 | 简单 | 帖子 | eq_ref | 初级 | 初级 | 4 | .posts_tags.post_id | 1 | | +----+-------------+------------+--------+-------- ------------------+----------------------------+---- -----+----------+--------+-------------- ---------------------------------------------+ 2 行(0.00 秒)
我需要定义什么样的索引来避免mysql使用filesort?当 order 字段不在 where 子句中时是否可能?
更新:分析结果:
mysql> 显示查询 1 的配置文件; +--------------------------------+----------+ | 状态 | 持续时间 | +--------------------------------+----------+ | 出发| 0.000027 | | 检查查询缓存中的查询 | 0.037953 | | 开桌 | 0.000028 | | 系统锁 | 0.010382 | | 表锁 | 0.023894 | | 初始化 | 0.000057 | | 优化 | 0.010030 | | 统计 | 0.000026 | | 准备| 0.000018 | | 创建 tmp 表 | 0.128619 | | 执行 | 0.000008 | | 复制到 tmp 表 | 1.819463 | | 排序结果 | 0.001092 | | 发送数据 | 0.004239 | | 结束 | 0.000012 | | 删除 tmp 表 | 0.000885 | | 结束 | 0.000006 | | 结束 | 0.000005 | | 查询结束 | 0.000006 | | 将结果存储在查询缓存中 | 0.000005 | | 释放物品 | 0.000021 | | 关闭表| 0.000013 | | 记录慢查询 | 0.000004 | | 清理| 0.000006 | +--------------------------------+----------+
更新2:
真实查询(更多布尔字段,更多无用索引)
SELECT `posts`.* FROM `posts` INNER JOIN `posts_tags` ON `posts`.id = `posts_tags`.post_id WHERE ((`posts_tags`.tag_id = 7971)) AND (((posts.trashed = 0) AND (`posts`.`published` = 1 AND `posts`.`clan_private` = 0)) AND ((`posts_tags`.tag_id = 7971))) ORDER BY created_at DESC LIMIT 0, 10;
空集(1.25 秒)
没有 ORDER BY — 0.01 秒。
+----+-------------+------------+--------+-------- ---------------------------------+---------------- -------+----------+----------+--------+-- ------------------------------------+ | 编号 | 选择类型 | 表| 类型 | 可能的键 | 关键 | key_len | 参考 | 行 | 额外 | +----+-------------+------------+--------+-------- ---------------------------------+---------------- -------+----------+----------+--------+-- ------------------------------------+ | 1 | 简单 | 帖子标签 | 索引 | index_posts_tags_on_post_id_and_tag_id | index_posts_tags_... | 10 | 空 | 23988 | 使用哪里;使用索引 | | 1 | 简单 | 帖子 | eq_ref | 初级,index_posts_on_trashed_and_crea | 初级 | 4 | .posts_tags.post_id | 1 | 使用位置 | +----+-------------+------------+--------+-------- ---------------------------------+---------------- -------+----------+----------+--------+-- ------------------------------------+
解决方案
- 查询更新为“ORDER BY posts_tags.created_at DESC”(应用代码中的两个小改动)
- 添加的索引:index_posts_tags_on_created_at。
就这样!