10.2.1.16 Оптимизация ORDER BY
В этом разделе описывается, когда MySQL может использовать индекс для удовлетворения условия ORDER BY, операцию filesort, используемую, когда индекс не может быть использован, и информацию плана выполнения, доступную от оптимизатора о ORDER BY.
Запрос ORDER BY с и без LIMIT может возвращать строки в разном порядке, как обсуждается в разделе 10.2.1.19 «Оптимизация запросов LIMIT».
Использование индексов для удовлетворения ORDER BY
В некоторых случаях MySQL может использовать индекс для удовлетворения условия ORDER BY и избежать дополнительной сортировки, связанной с выполнением операции filesort.
Индекс также может быть использован, даже если ORDER
BY не точно соответствует индексу, пока все неиспользуемые части индекса и все дополнительные столбцы ORDER BY являются константами в условии WHERE. Если индекс не содержит все столбцы, доступные запросом, индекс используется только в случае, если доступ к индексу дешевле, чем другие методы доступа.
Предполагая, что есть индекс по (, следующие запросы могут использовать индекс для решения части key_part1,
key_part2)ORDER BY. Будет ли оптимизатор действительно это делать зависит от того, является ли чтение индекса более эффективным, чем сканирование таблицы, если также необходимо прочитать столбцы, отсутствующие в индексе.
-
В этом запросе индекс по
(позволяет оптимизатору избежать сортировки:key_part1,key_part2)SELECT * FROM t1 ORDER BY
key_part1,key_part2;Однако, запрос использует
SELECT *, который может выбрать больше столбцов, чемkey_part1иkey_part2. В этом случае сканирование всего индекса и поиск строк таблицы для нахождения столбцов, не входящих в индекс, может быть дороже, чем сканирование таблицы и сортировка результатов. Если это так, оптимизатор, вероятно, не использует индекс. ЕслиSELECT *выбирает только столбцы индекса, индекс используется и сортировка избегается.Если
t1— это таблицаInnoDB, первичный ключ таблицы неявно входит в индекс, и индекс может быть использован для решенияORDER BYдля этого запроса:SELECT
pk,key_part1,key_part2FROM t1 ORDER BYkey_part1,key_part2; -
В этом запросе
key_part1является константой, поэтому все строки, доступные через индекс, находятся в порядкеkey_part2, и индекс по(избегает сортировки, если условиеkey_part1,key_part2)WHEREдостаточно селективно, чтобы сделать сканирование диапазона по индексу дешевле, чем сканирование таблицы:SELECT * FROM t1 WHERE
key_part1=constantORDER BYkey_part2; -
В следующих двух запросах использование индекса аналогично тем же запросам без
DESC, показанных ранее:SELECT * FROM t1 ORDER BY
key_part1DESC,key_part2DESC; SELECT * FROM t1 WHEREkey_part1=constantORDER BYkey_part2DESC; -
Два столбца в
ORDER BYмогут сортироваться в одном направлении (обаASCили обаDESC) или в противоположных направлениях (одинASC, другойDESC). Условием использования индекса является то, что индекс должен иметь ту же однородность, но не обязательно то же фактическое направление.Если запрос смешивает
ASCиDESC, оптимизатор может использовать индекс по столбцам, если индекс также использует соответствующие смешанные возрастающие и убывающие столбцы:SELECT * FROM t1 ORDER BY
key_part1DESC,key_part2ASC;Оптимизатор может использовать индекс по (
key_part1,key_part2), еслиkey_part1убывающий, аkey_part2возрастающий. Он также может использовать индекс по этим столбцам (с обратным сканированием), еслиkey_part1возрастающий, аkey_part2убывающий. См. раздел 10.3.13 «Убывающие индексы». -
В следующих двух запросах
key_part1сравнивается с константой. Индекс используется, если условиеWHEREдостаточно селективно, чтобы сделать сканирование диапазона по индексу дешевле, чем сканирование таблицы:SELECT * FROM t1 WHERE
key_part1>constantORDER BYkey_part1ASC; SELECT * FROM t1 WHEREkey_part1<constantORDER BYkey_part1DESC; -
В следующем запросе
ORDER BYне называетkey_part1, но все выбранные строки имеют постоянное значениеkey_part1, поэтому индекс все еще может быть использован:SELECT * FROM t1 WHERE
key_part1=constant1ANDkey_part2>constant2ORDER BYkey_part2;
В некоторых случаях MySQL не может использовать индексы для решения ORDER BY, хотя он может по-прежнему использовать индексы для поиска строк, которые соответствуют условию WHERE. Примеры:
-
Запрос использует
ORDER BYпо разным индексам:SELECT * FROM t1 ORDER BY
key1,key2; -
Запрос использует
ORDER BYпо несмежным частям индекса:SELECT * FROM t1 WHERE
key2=constantORDER BYkey1_part1,key1_part3; -
Индекс, используемый для извлечения строк, отличается от того, который используется в
ORDER BY:SELECT * FROM t1 WHERE
key2=constantORDER BYkey1; -
Запрос использует
ORDER BYс выражением, которое включает термины, помимо имени столбца индекса:SELECT * FROM t1 ORDER BY ABS(
key); SELECT * FROM t1 ORDER BY -key; Запрос объединяет много таблиц, и столбцы в
ORDER BYне все из первой неконстантной таблицы, которая используется для получения строк. (Это первая таблица в выводеEXPLAIN, у которой нет типа соединенияconst).В запросе есть разные
ORDER BYиGROUP BYвыражения.Существует индекс только на префиксе столбца, указанного в условии
ORDER BY. В этом случае индекс не может полностью разрешить порядок сортировки. Например, если индексированы только первые 10 байтов столбцаCHAR(20), индекс не может различать значения после 10-го байта, и требуетсяfilesort.Индекс не хранит строки в порядке. Например, это верно для индекса
HASHв таблицеMEMORY.
Доступность индекса для сортировки может зависеть от использования псевдонимов столбцов. Предположим, что столбец t1.a индексирован. В этом операторе имя столбца в списке выбора — a. Оно относится к t1.a, как и ссылка на a в ORDER BY, поэтому индекс по t1.a может быть использован:
SELECT a FROM t1 ORDER BY a;
В этом операторе имя столбца в списке выбора также a, но это псевдоним. Оно относится к ABS(a), как и ссылка на a в ORDER BY, поэтому индекс по t1.a не может быть использован:
SELECT ABS(a) AS a FROM t1 ORDER BY a;
В следующем операторе ORDER BY относится к имени, которое не является именем столбца в списке выбора. Но есть столбец в t1 с именем a, поэтому ORDER
BY относится к t1.a, и индекс по t1.a может быть использован. (Конечно, итоговый порядок сортировки может быть совершенно другим по сравнению с порядком для ABS(a).)
SELECT ABS(a) AS b FROM t1 ORDER BY a;
Ранее (MySQL 8.3 и ниже) GROUP BY сортировался неявно при определенных условиях. В MySQL 8.4 этого больше нет, поэтому указание ORDER BY NULL в конце для подавления неявной сортировки (как это делалось ранее) больше не требуется. Однако результаты запросов могут отличаться от предыдущих версий MySQL. Чтобы получить заданный порядок сортировки, укажите условие ORDER BY.
Использование filesort для удовлетворения ORDER BY
Если индекс не может быть использован для удовлетворения условия ORDER
BY, MySQL выполняет операцию filesort, которая считывает строки таблицы и сортирует их. Операция filesort представляет собой дополнительную фазу сортировки при выполнении запроса.
Для получения памяти для операций filesort оптимизатор выделяет буферы памяти по мере необходимости, до размера, указанного переменной sort_buffer_size. Это позволяет пользователям устанавливать значение sort_buffer_size для более крупных значений, чтобы ускорить сортировку больших объемов данных, не беспокоясь о чрезмерном использовании памяти для небольшой сортировки. (Этот выигрыш может не произойти для нескольких одновременных сортировок в Windows, у которой слабая многопоточная malloc.)
Операция filesort использует временные файлы на диске по мере необходимости, если результирующий набор слишком велик для размещения в памяти. Некоторые типы запросов особенно подходят для полностью выполняемых в памяти операций filesort. Например, оптимизатор может использовать filesort для эффективной обработки в памяти без временных файлов операции ORDER BY для запросов (и подзапросов) следующего вида:
SELECT ... FROM single_table ... ORDER BY non_index_column [DESC] LIMIT [M,]N;
Такие запросы распространены в веб-приложениях, которые отображают только несколько строк из большего набора результатов. Примеры:
SELECT col1, ... FROM t1 ... ORDER BY name LIMIT 10;
SELECT col1, ... FROM t1 ... ORDER BY RAND() LIMIT 15;
Влияние на оптимизацию ORDER BY
Для увеличения скорости ORDER BY, проверьте, может ли MySQL использовать индексы вместо дополнительной фазы сортировки. Если это невозможно, попробуйте следующие стратегии:
-
Увеличьте значение переменной
sort_buffer_size. В идеале, значение должно быть достаточно большим, чтобы весь результирующий набор поместился в буфер сортировки (чтобы избежать записи на диск и объединения проходов).Учитывайте, что размер значений столбцов, хранящихся в буфере сортировки, зависит от значения переменной
max_sort_length. Например, если кортежи хранят значения длинных строковых столбцов, и вы увеличиваете значениеmax_sort_length, размер кортежей буфера сортировки также увеличивается и может потребовать увеличения значенияsort_buffer_size.Для отслеживания количества объединительных проходов (для объединения временных файлов) проверьте переменную состояния
Sort_merge_passes. Увеличьте значение переменной
read_rnd_buffer_size, чтобы считывать больше строк за раз.Измените переменную
tmpdirтак, чтобы она указывала на файловую систему с большим количеством свободного места. Значение переменной может содержать несколько путей, которые используются по круговому принципу; вы можете использовать эту функцию, чтобы распределить нагрузку по нескольким каталогам. Разделяйте пути двоеточием (:) в Unix и точкой с запятой (;) в Windows. Пути должны указывать на каталоги в файловых системах, расположенных на разных физических дисках, а не на разных разделах одного диска.
Информация о плане выполнения ORDER BY, доступная
С помощью EXPLAIN (см. Раздел 10.8.1, «Оптимизация запросов с помощью EXPLAIN»), вы можете проверить, может ли MySQL использовать индексы для решения условия ORDER
BY:
Кроме того, если выполняется операция filesort, выходные данные трассировки оптимизатора включают блок filesort_summary. Например:
"filesort_summary": {
"rows": 100,
"examined_rows": 100,
"number_of_tmp_files": 0,
"peak_memory_used": 25192,
"sort_mode": "<sort_key, packed_additional_fields>"
}
peak_memory_used указывает максимальную память, используемую в любой момент во время сортировки. Это значение до, но не обязательно равно значению переменной sort_buffer_size. Оптимизатор выделяет память буфера сортировки поэтапно, начиная с небольшого объема и добавляя больше по мере необходимости, до sort_buffer_size байт.)
Значение sort_mode предоставляет информацию о содержании кортежей в буфере сортировки:
<sort_key, rowid>: Это указывает, что кортежи буфера сортировки являются парами, которые содержат значение ключа сортировки и идентификатор строки исходной строки таблицы. Кортежи сортируются по значению ключа сортировки, а идентификатор строки используется для считывания строки из таблицы.<sort_key, additional_fields>: Это указывает, что кортежи буфера сортировки содержат значение ключа сортировки и столбцы, на которые ссылается запрос. Кортежи сортируются по значению ключа сортировки, а значения столбцов считываются непосредственно из кортежа.<sort_key, packed_additional_fields>: Подобно предыдущему варианту, но дополнительные столбцы упакованы плотно друг к другу вместо использования кодирования с фиксированной длиной.
EXPLAIN не различает, выполняет ли оптимизатор операцию filesort в памяти или нет. Использование операции filesort в памяти может быть видно в выводе трассировки оптимизатора. Ищите filesort_priority_queue_optimization. Дополнительную информацию о трассировке оптимизатора см. в Разделе 10.15, «Отслеживание оптимизатора».
© 2025 Oracle
Licensed under the GPLv2 License.