MySQL не використовує індекси під час приєднання до іншої таблиці


11

У мене дві таблиці, перша таблиця містить усі статті / повідомлення в блозі в межах CMS. Деякі з цих статей можуть також з’являтися в журналі, і в цьому випадку вони мають зв'язок із зовнішнім ключем до іншої таблиці, що містить конкретну інформацію журналу.

Ось спрощена версія синтаксису таблиці створення для цих двох таблиць із викресленими несуттєвими рядками:

CREATE TABLE `base_article` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `date_published` datetime DEFAULT NULL,
  `title` varchar(255) NOT NULL,
  `description` text,
  `content` longtext,
  `is_published` int(11) NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`),
  KEY `base_article_date_published` (`date_published`),
  KEY `base_article_is_published` (`is_published`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

CREATE TABLE `mag_article` (
    `basearticle_ptr_id` int(11) NOT NULL,
    `issue_slug` varchar(8) DEFAULT NULL,
    `rubric` varchar(75) DEFAULT NULL,
    PRIMARY KEY (`basearticle_ptr_id`),
    KEY `mag_article_issue_slug` (`issue_slug`),
    CONSTRAINT `basearticle_ptr_id_refs_id` FOREIGN KEY (`basearticle_ptr_id`) REFERENCES `base_article` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

CMS містить близько 250 000 статей, і я написав простий скрипт Python, який можна використовувати для заповнення тестової бази даних із зразковими даними, якщо вони хочуть повторити цю проблему локально.

Якщо я вибираю одну з цих таблиць, у MySQL немає проблем зі швидким підбором відповідного індексу або швидким завантаженням статей. Однак, коли дві таблиці з'єднуються у простому запиті, наприклад:

SELECT * FROM `base_article` 
INNER JOIN `mag_article` ON (`mag_article`.`basearticle_ptr_id` = `base_article`.`id`)
WHERE is_published = 1
ORDER BY `base_article`.`date_published` DESC
LIMIT 30

MySQL не вдається вибрати відповідний запит та продуктивність. Ось відповідне розширене пояснення (час виконання якого перевищує секунду):

+----+-------------+--------------+--------+-----------------------------------+---------+---------+----------------------------------------+-------+----------+---------------------------------+
| id | select_type |    table     |  type  |           possible_keys           |   key   | key_len |                  ref                   | rows  | filtered |              Extra              |
+----+-------------+--------------+--------+-----------------------------------+---------+---------+----------------------------------------+-------+----------+---------------------------------+
|  1 | SIMPLE      | mag_article  | ALL    | PRIMARY                           | NULL    | NULL    | NULL                                   | 23830 | 100.00   | Using temporary; Using filesort |
|  1 | SIMPLE      | base_article | eq_ref | PRIMARY,base_article_is_published | PRIMARY | 4       | my_test.mag_article.basearticle_ptr_id |     1 | 100.00   | Using where                     |
+----+-------------+--------------+--------+-----------------------------------+---------+---------+----------------------------------------+-------+----------+---------------------------------+
  • EDIT SEPT 30: Я можу видалити WHEREпункт цього запиту, але EXPLAINвсе ще виглядає так само, і запит все ще повільний.

Одне потенційне рішення - примусити індекс. Запуск того ж запиту з FORCE INDEX (base_articel_date_published)результатами запиту, який виконується приблизно за 1,6 мілісекунд.

+----+-------------+--------------+--------+---------------+-----------------------------+---------+-------------------------+------+-----------+-------------+
| id | select_type |    table     |  type  | possible_keys |             key             | key_len |           ref           | rows | filtered  |    Extra    |
+----+-------------+--------------+--------+---------------+-----------------------------+---------+-------------------------+------+-----------+-------------+
|  1 | SIMPLE      | base_article | index  | NULL          | base_article_date_published |       9 | NULL                    |   30 | 833396.69 | Using where |
|  1 | SIMPLE      | mag_article  | eq_ref | PRIMARY       | PRIMARY                     |       4 | my_test.base_article.id |    1 | 100.00    |             |
+----+-------------+--------------+--------+---------------+-----------------------------+---------+-------------------------+------+-----------+-------------+

Я вважаю за краще не змушувати індексу на цей запит, якщо я можу його уникнути, з кількох причин. Найголовніше, що цей базовий запит може бути відфільтрований / модифікований різними способами (наприклад, фільтрування за допомогою issue_slug), після чого base_article_date_publishedможе бути вже не найкращим індексом для використання.

Чи може хтось запропонувати стратегію підвищення ефективності цього запиту?


якщо стовпець "is_publisher" зберігає лише два-три значення, ви могли б справді скинути цей індекс KEY base_article_is_published( is_published) .. мені здається, що це булевий тип ..
Raymond Nijland

редагував відповідь
Raymond Nijland

Відповіді:


5

Що стосується цього, це повинно усунути необхідність "Використовувати тимчасовий; Використання файлового ряду", оскільки дані вже в потрібному вигляді.

Вам потрібно знати хитрість, чому для видалення цієї потреби для MySQL потрібне "Використання тимчасових; Використання файлових рядів"

Див. Другу sqlfriddle для пояснення про усунення потреби

SELECT
      *
    FROM base_article

    STRAIGHT_JOIN 
      mag_article
    ON
      (mag_article.basearticle_ptr_id = base_article.id)

    WHERE
      base_article.is_published = 1

    ORDER BY
      base_article.date_published DESC

див. http://sqlfiddle.com/#!2/302710/2

Працює досить добре, мені це знадобилося і деякий час тому для таблиць країни / міста дивіться демонстрацію тут із прикладом даних http://sqlfiddle.com/#!2/b34870/41

Відредагований ви також можете проаналізувати цю відповідь, якщо base_article.is_publish = 1 завжди повертає 1 запис, як ваш пояснив пояснив, що таблиця INNER JOIN надає таблицю, може дати кращу ефективність, як запити у відповіді нижче

/programming/18738483/mysql-slow-query-using-filesort/18774937#18774937


Відповідь, що рятує життя! Я використовував JOINлише, але MySQL не збирав індекс. Дякую Реймонд
Максимус

4

РЕФАКТОР ЗАПИТАННЯ

SELECT * FROM
(SELECT * FROM base_article
WHERE is_published = 1
ORDER BY date_published LIMIT 30) A
INNER JOIN mag_article B
ON A.id = B.basearticle_ptr_id;

або

SELECT B.*,C.* FROM
(SELECT id FROM base_article
WHERE is_published = 1
ORDER BY date_published LIMIT 30) A
LEFT JOIN base_article ON A.id = B.id
LEFT JOIN mag_article C ON B.id = C.basearticle_ptr_id;

МОДФІКУЙТЕ СВОЇ ІНДЕКСИ

ALTER TABLE base_article DROP INDEX base_article_is_published;
ALTER TABLE base_article ADD INDEX ispub_datepub_index (is_published,date_published);

СПРОБУВАТИ !!!


Refactor: Не працює, боюся, тому що LIMIT 30знаходиться в підзапиті (не всі ці 30 рядків також будуть в mag_articlesтаблиці). Якщо я переміщу на LIMITзовнішній запит, продуктивність буде такою ж, як і в оригіналі. Змінення індексів: MySQL також не використовує цей індекс. Видалення WHEREпункту з мого початкового запиту, схоже, не має значення.
Joshmaker

Другий метод рефактора працював неймовірно добре, час запиту було скорочено різко з 8 секунд до 0,3 секунди в моїй таблиці ... дякую сер !!
andreszs
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.