Spec-Zone.ru › MySQL 5.7

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

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

Запрос ORDER BY с и без LIMIT может возвращать строки в разном порядке, как обсуждается в разделе 8.2.1.17 «Оптимизация запросов 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 является таблицей с первичным ключом, первичный ключ таблицы неявно входит в индекс, и индекс может быть использован для решения 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;
    
  • В следующих двух запросах 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;
    
  • Запрос сочетает ASC и DESC:

    SELECT * FROM t1 ORDER BY key_part1 DESC, key_part2 ASC;
    
  • Индекс, используемый для извлечения строк, отличается от того, который используется в 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 сортирует запросы 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:

  • Если столбец 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,
  "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.
https://docs.oracle.com/cd/E17952_01/mysql-5.7-en/order-by-optimization.html

Spec-Zone.ru

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