Как MySQL ведет себя в случаях JOIN, когда заданы ORDER BY и LIMIT и на самом деле требуется только небольшое количество строк? - PullRequest
2 голосов
/ 18 августа 2010

Предположим, у меня есть следующие таблицы:

CREATE TABLE Game (
    GameID INT UNSIGNED NOT NULL,
    GameType TINYINT UNSIGNED NOT NULL,
    PRIMARY KEY (GameID),
    INDEX Index_GameType (GameType, GameID)
) ENGINE=INNODB

CREATE TABLE HighScore (
    Game INT UNSIGNED NOT NULL,
    Score SMALLINT UNSIGNED,
    PRIMARY KEY (Game),
    INDEX Index_Score (Score, Game),
    CONSTRAINT Constr_Score_Game_fk
        FOREIGN KEY Score_Game_fk (Game) REFERENCES Game (GameID)
) ENGINE=INNODB

(Это уменьшенные версии реальных таблиц, с которыми я работаю. В реальных таблицах больше столбцов и индексов. Вышеприведенные таблицы отражают основные особенности ситуации.)

(Предполагается, что число различных GameTypes невелико, поэтому индекс Index_GameType не очень избирателен.)

Предположим, я запускаю следующий запрос:

SELECT
    HighScore.Score
FROM
    HighScore
    JOIN Game ON HighScore.Game = Game.GameID
WHERE
    Game.GameType = 42
ORDER BY
    HighScore.Score DESC
LIMIT 50

Глядя на этот запрос и структуру таблицы, мы, вероятно, можем согласиться с тем, что разумно было бы сканировать таблицу HighScore и объединять строки, пока не будет найдено 50, для которых выполняется условие WHERE. Тем не менее, EXPLAIN для меня показал (используя мои настоящие, более сложные таблицы), что MySQL фактически планирует искать все строки в Game, удовлетворяющие условию WHERE, объединять их с HighScore и выполнять сортировку файлов, чтобы получить строки в отсортированном порядке. 1011 *

Казалось бы, разумно указать вместо STRAIGHT_JOIN в приведенном выше запросе. Теперь вывод EXPLAIN указывает, что первая таблица, HighScore, «использует индекс» (как и ожидалось), но число строк, о которых сообщается, представляется количеством строк в таблице HighScore. Должен ли я считать, что это означает, что MySQL планирует взять практически весь индекс, объединить каждую строку в этом индексе с другой таблицей и только затем выбросить строки ниже 50 лучших? Это кажется смешным, но я не уверен, что так оно и будет на самом деле. У кого-нибудь есть идеи?

Ответы [ 2 ]

2 голосов
/ 18 августа 2010

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

Если вы добавите предложение STRAIGHT_JOIN, вы заставите MySQL взять каждую запись из HighScore (используя индекс для Score), найдите соответствующую запись в Game, проверьте, является ли она 42, и вернуть (или пренебречь) это.

Поскольку MySQL не может заранее определить, сколько записей будет соответствовать, он примет наихудшее значение и просто покажет общее количество HighScore записей в плане.

На самом деле, запрос будет остановлен после 50 возвращаемых записей.

0 голосов
/ 18 августа 2010

Этот ответ расширяет информацию, предоставленную Кассной. Я использую ответ, а не комментарий, чтобы иметь больше места.

Я протестировал выполнение запроса с предложением LIMIT и без него, как это было предложено Quassnoi. Поскольку я использую InnoDB, а не MyISAM, для получения количества запросов на чтение я использовал следующий запрос:

select
    variable_value
from
    information_schema.GLOBAL_STATUS
where
    variable_name = 'innodb_buffer_pool_read_requests';

Перед выполнением любых запросов это дало 87131. После выполнения запроса без предложения LIMIT, оно дало 170381. После выполнения запроса с предложением LIMIT, это дало 175315.

Таким образом, число запросов на чтение, включенных в запрос без LIMIT, по-видимому, составило 170381 - 87131 = 83250, в то время как число запросов на чтение, задействованных в запросе с LIMIT, было 175315 - 170381 = 4934. цифры появились при повторении эксперимента. Эти числа, кажется, не соответствуют строкам, на самом деле я не уверен, что они соответствуют с точки зрения извлеченных данных *, но они, похоже, показывают, что достоверно меньше данных было получено с диска, когда LIMIT запрос был добавлен. Поэтому я склонен думать, что Quassnoi верен и что MySQL действительно использует разумную стратегию для извлечения ограниченного числа строк.

  • Количество запросов на чтение, участвующих в запросе no-LIMIT, примерно в 17 раз превышает число запросов из другого запроса, но возвращено гораздо больше 17 * 50 результатов, поэтому, похоже, оно не соответствует непосредственно числу результаты.
...