8.2.1.14 Оптимизация ORDER BY
В этом разделе описывается, когда MySQL может использовать индекс для удовлетворения условия ORDER BY, операция filesort, используемая, когда индекс не может быть использован, и информация о плане выполнения, доступная от оптимизатора относительно ORDER BY.
Запрос ORDER BY с и без LIMIT может возвращать строки в разном порядке, как обсуждается в разделе 8.2.1.17 «Оптимизация запросов 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является таблицей с первичным ключом, первичный ключ таблицы неявно входит в индекс, и индекс может быть использован для решения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; -
В следующих двух запросах
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; -
Запрос сочетает
ASCиDESC:SELECT * FROM t1 ORDER BY
key_part1DESC,key_part2ASC; -
Индекс, используемый для извлечения строк, отличается от того, который используется в
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 сортирует запросы GROUP BY
так, как если бы вы также включили col1,
col2, ...ORDER BY
в запрос. Если вы включите явное условие col1,
col2, ...ORDER BY, содержащее тот же список столбцов, MySQL оптимизирует его, не снижая производительность, хотя сортировка всё равно выполняется.
Если запрос включает GROUP BY, но вы хотите избежать накладных расходов на сортировку результата, можно подавить сортировку, указав ORDER BY
NULL. Например:
INSERT INTO foo
SELECT a, COUNT(*) FROM bar GROUP BY a ORDER BY NULL;
Оптимизатор может по-прежнему выбрать сортировку для реализации операций группировки. ORDER BY NULL подавляет сортировку результата, а не предыдущую сортировку, выполняемую операциями группировки для определения результата.
GROUP BY неявно сортирует по умолчанию (то есть в отсутствие ASC или DESC указателей для столбцов GROUP
BY). Однако, полагаться на неявную сортировку GROUP BY (то есть сортировку в отсутствие ASC или DESC указателей) или явную сортировку для GROUP BY (то есть с использованием явных ASC или DESC указателей для столбцов GROUP BY) не рекомендуется. Для получения заданного порядка сортировки укажите условие ORDER BY.
Использование filesort для удовлетворения ORDER BY
Если индекс не может быть использован для удовлетворения условия ORDER
BY, MySQL выполняет операцию filesort, которая считывает строки таблицы и сортирует их. filesort представляет собой дополнительную фазу сортировки при выполнении запроса.
Для получения памяти для операций filesort оптимизатор выделяет фиксированное количество байтов sort_buffer_size сразу. Отдельные сессии могут по желанию изменять сессионное значение этой переменной, чтобы избежать избыточного использования памяти или выделить больше памяти по мере необходимости.
Операция 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, попробуйте уменьшить системную переменную max_length_for_sort_data до значения, достаточного для инициирования filesort. (Признаком установленного слишком большого значения этой переменной является сочетание высокой активности диска и низкой активности ЦП.)
Для повышения скорости ORDER BY, проверьте, может ли MySQL использовать индексы вместо дополнительной фазы сортировки. Если это невозможно, попробуйте следующие стратегии:
-
Увеличьте значение переменной
sort_buffer_size. В идеале значение должно быть достаточно большим, чтобы весь результат поместился в буфер сортировки (чтобы избежать записи на диск и слияния проходов), но как минимум оно должно быть достаточно большим, чтобы вместить 15 кортежей. (До 15 временных файлов диска сливаются, и в памяти должно быть место как минимум для одного кортежа на файл.)Учитывайте, что размер значений столбцов, хранящихся в буфере сортировки, зависит от значения системной переменной
max_sort_length. Например, если кортежи хранят значения длинных строковых столбцов, и вы увеличиваете значениеmax_sort_length, размер кортежей буфера сортировки также увеличивается и может потребовать увеличенияsort_buffer_size. Для значений столбцов, вычисленных в результате строковых выражений (например, тех, которые вызывают строковую функцию), алгоритмfilesortне может определить максимальную длину значений выражений, поэтому он должен выделятьmax_sort_lengthбайт для каждого кортежа.Для отслеживания количества проходов слияния (для слияния временных файлов) проверьте системную переменную
Sort_merge_passes. Увеличьте значение переменной
read_rnd_buffer_size, чтобы читать больше строк за один раз.Измените системную переменную
tmpdirна указание на специализированную файловую систему с большим объемом свободного места. Значение переменной может перечислять несколько путей, которые используются в режиме циклического обхода; вы можете использовать эту функцию для распределения нагрузки по нескольким каталогам. Разделяйте пути с помощью двоеточия (:) в Unix и точки с запятой (;) в Windows. Пути должны указывать на каталоги в файловых системах, расположенных на разных физических дисках, а не на разных разделах одного диска.
Доступная информация о плане выполнения ORDER BY
С помощью EXPLAIN (см. Раздел 8.8.1, «Оптимизация запросов с помощью EXPLAIN»), вы можете проверить, может ли MySQL использовать индексы для разрешения условия ORDER
BY:
Кроме того, если выполняется filesort, вывод трассировки оптимизатора включает блок filesort_summary. Например:
"filesort_summary": {
"rows": 100,
"examined_rows": 100,
"number_of_tmp_files": 0,
"sort_buffer_size": 25192,
"sort_mode": "<sort_key, packed_additional_fields>"
}
Значение sort_mode предоставляет информацию о содержимом кортежей в буфере сортировки:
<sort_key, rowid>: Это указывает, что кортежи буфера сортировки являются парами, которые содержат значение ключа сортировки и идентификатор строки исходной строки таблицы. Кортежи сортируются по значению ключа сортировки, а идентификатор строки используется для чтения строки из таблицы.<sort_key, additional_fields>: Это указывает, что кортежи буфера сортировки содержат значение ключа сортировки и столбцы, на которые ссылается запрос. Кортежи сортируются по значению ключа сортировки, а значения столбцов читаются непосредственно из кортежа.<sort_key, packed_additional_fields>: Подобно предыдущему варианту, но дополнительные столбцы упаковываются плотно вместе вместо использования кодирования с фиксированной длиной.
EXPLAIN не различает, выполняет ли оптимизатор filesort в памяти или нет. Использование памяти filesort можно увидеть в выводе трассировки оптимизатора. Ищите filesort_priority_queue_optimization. Сведения о трассировке оптимизатора см. в Разделе 8.15, «Отслеживание оптимизатора».
© 2025 Oracle
Licensed under the GPLv2 License.