9

我正在尝试优化此查询:

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 | 使用位置 |
+----+-------------+------------+--------+-------- ---------------------------------+---------------- -------+----------+----------+--------+-- ------------------------------------+

解决方案

  1. 查询更新为“ORDER BY posts_tags.created_at DESC”(应用代码中的两个小改动)
  2. 添加的索引:index_posts_tags_on_created_at。

就这样!

4

3 回答 3

3

您需要进行一些非规范化,并将 posts.created_at 字段复制到 post_tags 表中(我称之为 post_created_at,您可以随意命名):

CREATE TABLE `posts_tags` (
  `id` int(11) NOT NULL auto_increment,
  `post_id` int(11) default NULL,
  `tag_id` int(11) default NULL,
  `post_created_at` datetime default NULL,
  `created_at` datetime default NULL,
  `updated_at` datetime default NULL,
  PRIMARY KEY  (`id`),
  KEY `index_posts_tags_on_post_id_and_tag_id` (`post_id`,`tag_id`)
) ENGINE=InnoDB;

然后在posts_tags上添加一个索引

(tag_id, post_created_at)

这将允许查询以正确的顺序获取标签的所有帖子,而无需文件排序。

于 2010-06-10T19:45:27.827 回答
1

尝试将 KEY index_posts_tags_on_post_id_and_tag_id( post_id, tag_id) 更改为 KEY index_posts_tags_tag_id( tag_id) 并重新发布解释。

带有 Posts_Tags 的 TagID 的分布是什么?

于 2010-06-10T14:42:07.057 回答
0

您的键index_posts_on_created_at按升序排序,但您希望结果按降序排序

于 2010-06-10T14:38:01.250 回答