Spec-Zone.ru › MySQL 9.2

10.2.1.16 Оптимизация ORDER BY

В этом разделе описывается, когда MySQL может использовать индекс для удовлетворения условия ORDER BY, операцию filesort, используемую, когда индекс не может быть использован, и информацию о плане выполнения, доступную от оптимизатора, относительно ORDER BY.

Запрос ORDER BY с LIMIT и без них может возвращать строки в разном порядке, как обсуждалось в разделе 10.2.1.19 «Оптимизация запросов LIMIT».

  • Использование индексов для удовлетворения ORDER BY

  • Использование filesort для удовлетворения ORDER BY

  • Влияние на оптимизацию ORDER BY

  • Информация о плане выполнения ORDER BY

Использование индексов для удовлетворения 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_part2 FROM t1
      ORDER BY key_part1, key_part2;
    
  • В этом запросе key_part1 является константой, поэтому все строки, доступные через индекс, находятся в порядке key_part2, и индекс по (key_part1, key_part2) избегает сортировки, если условие WHERE достаточно селективно, чтобы сделать сканирование диапазона индекса дешевле, чем сканирование таблицы:

    SELECT * FROM t1
      WHERE key_part1 = constant
      ORDER BY key_part2;
    
  • В следующих двух запросах использование индекса аналогично тем же запросам без DESC, показанным ранее:

    SELECT * FROM t1
      ORDER BY key_part1 DESC, key_part2 DESC;
    
    SELECT * FROM t1
      WHERE key_part1 = constant
      ORDER BY key_part2 DESC;
    
  • Два столбца в ORDER BY могут сортироваться в одном направлении (оба ASC или оба DESC) или в противоположных направлениях (один ASC, другой DESC). Условием использования индекса является то, что индекс должен обладать той же однородностью, но не обязательно тем же фактическим направлением.

    Если запрос смешивает ASC и DESC, оптимизатор может использовать индекс по столбцам, если индекс также использует соответствующие смешанные возрастающие и убывающие столбцы:

    SELECT * FROM t1
      ORDER BY key_part1 DESC, key_part2 ASC;
    

    Оптимизатор может использовать индекс по (key_part1, key_part2), если key_part1 убывающий, а key_part2 возрастающий. Он также может использовать индекс по этим столбцам (с обратным сканированием), если key_part1 возрастающий, а key_part2 убывающий. См. раздел 10.3.13 «Убывающие индексы».

  • В следующих двух запросах key_part1 сравнивается с константой. Индекс используется, если условие WHERE достаточно селективно, чтобы сделать сканирование диапазона индекса дешевле, чем сканирование таблицы:

    SELECT * FROM t1
      WHERE key_part1 > constant
      ORDER BY key_part1 ASC;
    
    SELECT * FROM t1
      WHERE key_part1 < constant
      ORDER BY key_part1 DESC;
    
  • В следующем запросе ORDER BY не называет key_part1, но все выбранные строки имеют постоянное значение key_part1, поэтому индекс всё равно может быть использован:

    SELECT * FROM t1
      WHERE key_part1 = constant1 AND key_part2 > constant2
      ORDER BY key_part2;
    

В некоторых случаях MySQL не может использовать индексы для решения ORDER BY, хотя он по-прежнему может использовать индексы для поиска строк, соответствующих условию WHERE. Примеры:

  • Запрос использует ORDER BY на разных индексах:

    SELECT * FROM t1 ORDER BY key1, key2;
    
  • Запрос использует ORDER BY на несмежных частях индекса:

    SELECT * FROM t1 WHERE key2=constant ORDER BY key1_part1, key1_part3;
    
  • Индекс, используемый для извлечения строк, отличается от индекса, используемого в ORDER BY:

    SELECT * FROM t1 WHERE key2=constant ORDER BY key1;
    
  • Запрос использует 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 9.1 и ниже) GROUP BY сортировался неявно при определённых условиях. В MySQL 9.2 этого больше нет, поэтому указание 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:

  • Если столбец Extra вывода EXPLAIN не содержит Using filesort, индекс используется, и операция filesort не выполняется.

  • Если столбец Extra вывода EXPLAIN содержит Using filesort, индекс не используется, и выполняется операция filesort.

Кроме того, если выполняется операция 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.
https://docs.oracle.com/cd/E17952_01/mysql-9.2-en/order-by-optimization.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API