MySQL: где существует VS, где id в [производительности] - PullRequest
0 голосов
/ 09 мая 2019

Этот вопрос также существует здесь: Плохо, где есть производительность в Laravel ... но без ответа.

Подобная ситуация произошла со мной, как и с автором этого вопроса:

  • replays таблица имеет 4M строк
  • players таблица имеет 40M строк

Этот запрос использует where exists, и для его завершения требуется много времени (70 с):

select * from `replays` 
where exists (
    select * from `players` 
    where `replays`.`id` = `players`.`replay_id` 
      and `battletag_name` = 'test') 
order by `id` asc 
limit 100;

, но при изменении на where id in вместо where exists - это намного быстрее (0,4 с):

select * from `replays` 
where id in (
    select replay_id from `players` 
    where `battletag_name` = 'test') 
order by `id` asc 
limit 100;

MySQL (InnoDB) используется.

Я хотел бы понять, почему между where exists VS where id in такая большая разница в производительности - из-за того, как работает MySQL? Я ожидал, что вариант «существует» будет быстрее, потому что MySQL просто проверит, существуют ли соответствующие строки ... но я ошибся (возможно, я не понимаю, как в этом случае работает «существует»).

Ответы [ 2 ]

2 голосов
/ 09 мая 2019

Вы должны показать планы выполнения.

Чтобы оптимизировать exists, вам нужен индекс для players(replay_id, battletag_name). Индекс на replays(id) также должен помочь, но если id является первичным ключом, индекс уже есть.

1 голос
/ 09 мая 2019

Гордон имеет хороший ответ.Дело в том, что производительность зависит от множества различных факторов, включая структуру / схему базы данных и объем данных.

В качестве приблизительного ориентира, подзапрос exists будет выполняться один раз для каждой строки в replays, а подзапрос in будет выполняться один раз, чтобы получить результаты подзапроса.-query, и затем эти результаты будут искать для каждой строки в replays.

Таким образом, с exists, чем лучше путь индексации / доступа, тем быстрее он будет работать.Без соответствующих индексов он будет просто читать все строки, пока не найдет совпадение.Для каждой строки в replays.Для строк без совпадений это будет заканчиваться чтением таблицы players каждый раз.Даже строки с совпадениями могут прочитать значительное число players перед тем, как найти совпадение.

С in чем меньше набор результатов из подзапроса, тем быстрее он будет выполняться.Для тех, у кого нет совпадений, достаточно быстро проверить небольшие строки подзапроса, чтобы получить ответ.Тем не менее, вы не получаете преимущества от индексов (если это работает таким образом), поэтому для большого результирующего набора из подзапроса необходимо прочитать каждую строку в подвыборке, прежде чем решить, что при отсутствии совпадения.

Тем не менее, оптимизаторы баз данных довольно умны и не всегда оценивают запросы точно так, как вы их запрашиваете, поэтому для проверки наилучшего подхода важно проверить планы выполнения и самостоятельно протестировать себя.Нет ничего необычного в том, что можно ожидать, что определенный путь выполнения только обнаружит, что оптимизатор выбрал другой метод выполнения в зависимости от того, как он ожидает, что данные будут выглядеть.

...