Медленный запрос Mysql: ВНУТРЕННЕЕ ОБЪЕДИНЕНИЕ + ORDER BY вызывает filesort

Я пытаюсь оптимизировать этот запрос:

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 используют "filesort", таким образом, это становится довольно медленным. Я пытался использовать различные индексы, никакую удачу.

CREATE TABLE `posts` (
  `id` int(11) NOT NULL auto_increment,
  `created_at` datetime default NULL,
  PRIMARY KEY  (`id`),
  KEY `index_posts_on_created_at` (`created_at`),
  KEY `for_tags` (`trashed`,`published`,`clan_private`,`created_at`)
) ENGINE=InnoDB AUTO_INCREMENT=44390 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci

CREATE TABLE `posts_tags` (
  `id` int(11) NOT NULL auto_increment,
  `post_id` int(11) default NULL,
  `tag_id` int(11) 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 AUTO_INCREMENT=63175 DEFAULT CHARSET=utf8
+----+-------------+------------+--------+--------------------------+--------------------------+---------+---------------------+-------+-----------------------------------------------------------+
| id | select_type | table      | type   | possible_keys            | key                      | key_len | ref                 | rows  | Extra                                                     |
+----+-------------+------------+--------+--------------------------+--------------------------+---------+---------------------+-------+-----------------------------------------------------------+
|  1 | SIMPLE      | posts_tags | index  | index_post_id_and_tag_id | index_post_id_and_tag_id | 10      | NULL                | 24159 | Using where; Using index; Using temporary; Using filesort | 
|  1 | SIMPLE      | posts      | eq_ref | PRIMARY                  | PRIMARY                  | 4       | .posts_tags.post_id |     1 |                                                           | 
+----+-------------+------------+--------+--------------------------+--------------------------+---------+---------------------+-------+-----------------------------------------------------------+
2 rows in set (0.00 sec)

Какой индекс я должен определить для предотвращения mysql, использующего filesort? Действительно ли возможно, когда поле порядка не находится в где пункт?

обновление: Профилирование результатов:

mysql> show profile for query 1;
+--------------------------------+----------+
| Status                         | Duration |
+--------------------------------+----------+
| starting                       | 0.000027 | 
| checking query cache for query | 0.037953 | 
| Opening tables                 | 0.000028 | 
| System lock                    | 0.010382 | 
| Table lock                     | 0.023894 | 
| init                           | 0.000057 | 
| optimizing                     | 0.010030 | 
| statistics                     | 0.000026 | 
| preparing                      | 0.000018 | 
| Creating tmp table             | 0.128619 | 
| executing                      | 0.000008 | 
| Copying to tmp table           | 1.819463 | 
| Sorting result                 | 0.001092 | 
| Sending data                   | 0.004239 | 
| end                            | 0.000012 | 
| removing tmp table             | 0.000885 | 
| end                            | 0.000006 | 
| end                            | 0.000005 | 
| query end                      | 0.000006 | 
| storing result in query cache  | 0.000005 | 
| freeing items                  | 0.000021 | 
| closing tables                 | 0.000013 | 
| logging slow query             | 0.000004 | 
| cleaning up                    | 0.000006 | 
+--------------------------------+----------+

update2:

Реальный запрос (некоторые более булевы поля, больше бесполезных индексов)

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 с.


+----+-------------+------------+--------+-----------------------------------------+-----------------------+---------+---------------------+-------+--------------------------+
| id | select_type | table      | type   | possible_keys                           | key                   | key_len | ref                 | rows  | Extra                    |
+----+-------------+------------+--------+-----------------------------------------+-----------------------+---------+---------------------+-------+--------------------------+
|  1 | SIMPLE      | posts_tags | index  | index_posts_tags_on_post_id_and_tag_id  | index_posts_tags_...  | 10      | NULL                | 23988 | Using where; Using index | 
|  1 | SIMPLE      | posts      | eq_ref | PRIMARY,index_posts_on_trashed_and_crea | PRIMARY               | 4       | .posts_tags.post_id |     1 | Using where              | 
+----+-------------+------------+--------+-----------------------------------------+-----------------------+---------+---------------------+-------+--------------------------+

РЕШЕНИЕ

  1. Запрос, обновленный к "ORDER BY posts_tags.created_at DESC" (два небольших изменения в коде приложения)
  2. Индекс добавляется: index_posts_tags_on_created_at.

Это все!

9
задан OMG Ponies 11 July 2010 в 15:07
поделиться

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)

, что позволит запросу получить все сообщения для тега в правильном порядке без сортировки файлов.

3
ответ дан 3 November 2019 в 07:46
поделиться

ваш ключ index_posts_on_created_at отсортирован по возрастанию, но вы хотите, чтобы результаты были отсортированы по убыванию

0
ответ дан 3 November 2019 в 07:46
поделиться

Попробуйте изменить KEY index_posts_tags_on_post_id_and_tag_id (post_id,tag_id) на KEY index_posts_tags_tag_id (tag_id) и перепостить Объясните.

Каково распределение TagID среди Posts_Tags?

1
ответ дан 3 November 2019 в 07:46
поделиться
Другие вопросы по тегам:

Похожие вопросы: