Почему ORDER BY и LIMIT 1 так сильно замедляют запрос MySQL? - PullRequest
0 голосов
/ 09 июля 2020

У меня есть запрос, который выглядит очень простым. Однако, если я объединю ORDER BY и LIMIT, производительность снизится по шкале. Я нашел несколько вопросов об ограниченной производительности MySQL в больших таблицах, но я не думаю, что здесь дело обстоит именно так, потому что без LIMIT запрос работает find.

Вот запросы с возрастающей "сложностью" "

SELECT * FROM `mydata`.`mytable` WHERE ((token='XFRA1NMDU9XY') AND (section=210874));
/* Rows: 0  Time: 0,094 sec. */
SELECT * FROM `mydata`.`mytable` WHERE ((token='XFRA1NMDU9XY') AND (section=210874)) LIMIT 1;
/* Rows: 0  Time: 0,063 sec. */
SELECT * FROM `mydata`.`mytable` WHERE ((token='XFRA1NMDU9XY') AND (section=210874)) ORDER BY mailing;
/* Rows: 0  Time: 0,125 sec. */
SELECT * FROM `mydata`.`mytable` WHERE ((token='XFRA1NMDU9XY') AND (section=210874)) ORDER BY mailing LIMIT 1;
/* Rows: 0  Time: 45,500 sec. */

Обратите внимание на значительное увеличение времени запроса в последней строке. Это не исключение, но повторялось несколько раз. На самом деле у меня было время запроса 3 минуты и более для этого запроса, в то время как все остальное работало нормально.

Вот некоторые данные:

  • В таблице ~ 2.000.000 записей
  • Есть ~ 5.000 записей WHERE (section = 210874)
  • База данных работает на MySQL 8.0.20 и Ubuntu 20.04
  • Это таблица InnoDB, есть индекс для section (и других), но не для token

Вот структура таблицы:

CREATE TABLE `mytable` (
    `data` VARCHAR(32) NOT NULL COLLATE 'ascii_bin',
    `mailing` INT(10,0) NOT NULL,
    `token` VARCHAR(64) NULL DEFAULT NULL COLLATE 'ascii_bin',
    `section` INT(10,0) NOT NULL,
    `expiry` INT(10,0) NULL DEFAULT NULL,
    PRIMARY KEY (`data`) USING BTREE,
    INDEX `mailing_CS` (`mailing`) USING BTREE,
    INDEX `section_CS` (`section`) USING BTREE,
    CONSTRAINT `mailing_CS` FOREIGN KEY (`mailing`) REFERENCES `mydata`.`mailings` (`id`) ON UPDATE NO ACTION ON DELETE CASCADE,
    CONSTRAINT `section_CS` FOREIGN KEY (`section`) REFERENCES `mydata`.`sections` (`id`) ON UPDATE NO ACTION ON DELETE CASCADE
)
COLLATE='ascii_bin'
ENGINE=InnoDB
;

Любая идея, почему комбинация ORDER BY и LIMIT 1 имеет этот эффект? EXPPLAIN сообщает мне, что для первого (section_CS) и последнего запроса (mailing_CS) используются разные ключи (индексы).

Я могу, конечно, просто оставить LIMIT 1 , потому что на token не будет одной или нескольких строк. Но я хотел бы разобраться в основной проблеме.

Ответы [ 3 ]

2 голосов
/ 09 июля 2020

Я думаю, что MySQL пытается использовать индекс mailing_CS в последнем запросе, и этот индекс не оптимален.

Попробуйте этот запрос:

SELECT * 
FROM `mydata`.`mytable` USE INDEX (section_CS) IGNORE INDEX(mailing_CS) 
WHERE (
    (token = 'XFRA1NMDU9XY') AND 
    (section = 210874)
) 
ORDER BY mailing 
LIMIT 1

Также вы можете использовать составной индекс (раздел, рассылка) для этой таблицы.

0 голосов
/ 09 июля 2020
WHERE ((token='XFRA1NMDU9XY')
  AND (section=210874))
ORDER BY mailing LIMIT 1;

Требуется любой из этих составных индексов:

INDEX(token, section, mailing)
INDEX(section, token, mailing)

Любые более короткие индексы (совпадающие с крайними левыми столбцами) должны быть удалены, чтобы избежать путаницы.

Что касается того, почему вы получил эти тайминги ...

Первые два - WHERE a=1 AND b=2 - будут использовать либо INDEX(a), либо INDEX(b), но должны сканировать, чтобы проверить другое значение. Так что даже переключение на INDEX(a,b) или (b,a) ускорило бы их.

Для третьего запроса - WHERE a=1 AND b=2 ORDER BY c - это загадка; это должно было занять больше времени, чем 4-е.

ORDER BY без подходящего индекса требует следующего:

  1. Соберите все потенциальные строки (тысячи?)
  2. Сортировать их.
  3. Наконец очистить одну строку (LIMIT 1)

С любым из моих индексов четвертый запрос должен быть порядка миллисекунды.

0 голосов
/ 09 июля 2020

MySQL ORDER BY с LIMIT - наиболее распространенное использование ORDER BY в интерактивных приложениях с сортировкой больших наборов данных.

Убедитесь, что используется index . Очень важно, чтобы ORDER BY с LIMIT выполнялся без сканирования и сортировки полного набора результатов, поэтому для него важно использовать индекс - в этом случае сканирование диапазона индекса будет запущено, а выполнение запроса остановится, как только потребуется количество строк. сгенерировано.

Например, если я сделаю SELECT * FROM sites ORDER BY date_created DES C LIMIT 10; Я бы использовал index on (date_created), чтобы очень быстро получить набор результатов.

А что, если у меня есть что-то вроде SELECT * FROM sites WHERE category_id = 5 ORDER BY date_created DES C LIMIT 10;

В этом случае индекс по date_created также может работать, но может быть не самым лучшим эффективный - если это редкая категория, можно просканировать большую часть таблицы и найти 10 строк. Так что index on (category_id, date_created) будет лучше.

Индексирование может вам помочь !!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...