Документация MySQL
| Документация DHTML | Документация Smarty | SVG/VML Графика и JavaScript
| Документация bash |
| Глава 5. Оптимизация в MySQL | ||
|---|---|---|
| Пред. | След. | |
Глава 5. Оптимизация в MySQL
Содержание
- 5.1. Oбзор оптимизации
- 5.2. Оптимизация
SELECTи других запросов - 5.2.1. Синтаксис оператора
EXPLAIN(получение информации оSELECT) - 5.2.2. Оценка производительности запроса
- 5.2.3. Скорость выполнения запросов
SELECT - 5.2.4. Как MySQL оптимизирует выражения
WHERE - 5.2.5. Как MySQL оптимизирует
DISTINCT - 5.2.6. Как MySQL оптимизирует
LEFT JOINиRIGHT JOIN - 5.2.7. Как MySQL оптимизирует
ORDER BY - 5.2.8. Как MySQL оптимизирует
LIMIT - 5.2.9. Скорость выполнения запросов
INSERT - 5.2.10. Скорость выполнения запросов
UPDATE - 5.2.11. Скорость выполнения запросов
DELETE - 5.2.12. Другие советы по оптимизации
- 5.2.1. Синтаксис оператора
- 5.3. Вопросы блокировок
- 5.4. Оптимизация структуры базы данных
- 5.4.1. Конструктивные особенности MySQL
- 5.4.2. Сделайте объем данных как можно меньше
- 5.4.3. Использование индексов в MySQL
- 5.4.4. Индексы столбцов
- 5.4.5. Многостолбцовые индексы
- 5.4.6. Почему так много открытых таблиц?
- 5.4.7. Открытие и закрытие таблиц в MySQL
- 5.4.8. Недостатки создания множества таблиц в одной базе данных
- 5.5. Оптимизация сервера MySQL
- 5.6. Вопросы, относящиеся к диску
Оптимизация - сложная задача, потому что она, в конечном счете, требует понимания системы в целом. В отдельных случаях для выполнения локальной оптимизации достаточно знать систему или приложение не в полном объеме, однако чтобы сделать систему более оптимальной, нужно разбираться в том, как она устроена.
В этой главе будут рассмотрены различные способы оптимизации MySQL и представлены некоторые примеры ее выполнения. Не следует забывать, однако, что всегда можно найти некоторые дополнительные возможности сделать систему еще быстрее (хотя каждый следующий шаг в этом направлении будет даваться все труднее и труднее).
5.1. Oбзор оптимизации
- 5.1.1. Конструктивные ограничения MySQL/компромиссы
- 5.1.2. Вопросы переносимости
- 5.1.3. Для чего мы использовали MySQL?
- 5.1.4. Набор тестов MySQL (The MySQL Benchmark Suite)
- 5.1.5. Использование собственных тестов
Чтобы увеличить скорость системы, необходимо, разумеется, прежде всего разбираться в ее конструкции. Кроме того, нужно знать, какие функции будет выполнять система и какие "узкие места" в ней имеются.
Ниже приведен список наиболее часто встречающихся "узких мест":
Поиск данных на диске. Чтобы найти на диске какой-то фрагмент данных, требуется некоторое время. Для устройств выпуска 1999 года среднее время поиска составляет менее 10мс, так что теоретически можно выполнять приблизительно 100 операций поиска в секунду. Это время можно ненамного уменьшить, заменив диски более новыми. Для одной таблицы поиск на диске оптимизировать очень сложно. Такую оптимизацию можно выполнить путем распределения данных по нескольким дискам.
Дисковое чтение/запись. После выполнения поиска, когда найдена соответствующая позиция на диске, мы можем считать данные. Для устройств выпуска 1999 года производительность одного диска составляет около 10-20Мб/с. Дисковое чтение/запись легче оптимизировать, чем дисковый поиск, поэтому читать можно параллельно с нескольких дисков.
Циклы процессора. Когда мы помещаем данные в основную память (или если они уже находятся там), мы должны обработать их, чтобы получить результат. Наличие маленьких по сравнению с объемом ОЗУ таблиц - наиболее часто встречающийся лимитирующий фактор. Но в этом случае, в общем-то, скорость обработки маленьких таблиц значения не имеет.
Пропускная способность ОЗУ (memory bandwidth). Когда процессору требуется больше данных, чем может вместить его кэш, узким местом становится пропускная способность памяти. В большинстве систем это узкое место встречается редко, однако о нем нужно знать.
5.1.1. Конструктивные ограничения MySQL/компромиссы
При использовании обработчика
таблиц MyISAM MySQL применяет
очень быструю блокировку таблиц
(несколько потоков чтения/один
поток записи). Самая большая
проблема при использовании этого
типа таблиц возникает в случае
непрерывного потока обновлений в
сочетании с медленными выборками
из одной и той же таблицы. Если эта
проблема касается лишь некоторых
таблиц, можно использовать вместо
них таблицы другого типа. See
Глава 7, Типы таблиц MySQL.
MySQL может работать как с транзакционными так и с нетранзакционными таблицами. Чтобы обеспечить возможность нормальной работы с нетранзакционными таблицами (для которых невозможен откат, если что-нибудь произойдет не так, как надо), в MySQL существуют следующие правила:
Все столбцы имеют значения по умолчанию.
Если в столбец вставляется "неправильное" значение (например,
NULLв столбецNOT NULLили слишком большое числовое значение - в числовой столбец), MySQL не будет выводить сообщение об ошибке, а просто поместит в столбец "наиболее подходящее возможное значение". Для числовых значений это 0, наименьшие возможные значения или наибольшее возможное значение. Для строк это либо пустая строка, либо самая длинная строка, которая может быть в столбце.Все вычисляемые выражения возвращают значение, которое можно использовать вместо того, чтобы сигнализировать об ошибке. Например, выражение
1/0возвратитNULL
Существование приведенных выше правил объясняется тем, что перед началом выполнения запроса невозможно проверить, сможет ли он выполниться. Если проблема обнаружится после обновления нескольких строк, мы не можем выполнить полный откат, поскольку это может не поддерживаться типом таблицы. Остановиться в этот момент тоже нельзя, потому что тогда обновления будут выполнены наполовину, что является, вероятно, самым худшим возможным результатом. В данном случае лучше выбрать "наименьшее из зол", а затем продолжать, как будто ничего не произошло.
Отсюда следует, что MySQL нельзя использовать для проверки содержимого полей. Это нужно делать в приложении.
5.1.2. Вопросы переносимости
Поскольку все SQL-серверы поддерживают разные части стандарта SQL, то разработка переносимых SQL-приложений занимает время. Для очень простых запросов/вставок это достаточно просто, однако чем сложнее становится ваше приложение, тем сложнее делать запросы переносимыми. Если вы хотите чтобы ваше приложение работало максимально быстро с разными серверами SQL, задача еще более усложняется.
Чтобы сделать сложное приложение переносимым в области SQL, вам следует выбрать те SQL-серверы, с которыми оно должно работать.
Чтобы узнать, какие функции, типы и
ограничения существуют в
выбранных вами серверах, можно
воспользоваться приложением MySQL
crash-me. crash-me
пока еще далека от того, чтобы
тестировать все, что возможно, но
тем не менее, является достаточно
качественным сравнительным тестом
по более чем 450 характеристикам.
Например, если вы хотите использовать Informix или DB2, имена полей не должны быть длиннее 18 символов.
И тесты MySQL (MySQL benchmarks), и программа
crash-me являются
достаточно независимыми от
конкретной СУБД. Ознакомившись с
тем, как мы решили этот вопрос, вы
можете получить представление о
том, как следует писать
переносимые программы для работы с
базами данных. Тесты можно найти в
каталоге sql-bench в
поставке исходных текстов MySQL. Они
написаны на Perl с использованием
интерфейса DBI (который, кстати, уже
решает проблему получения доступа
к разным базам данных).
См. http://www.mysql.com/information/benchmarks.html - там находятся результаты тестов.
Как можно видеть по этим результатам, у каждой СУБД есть свои слабые стороны. Все они построены по-разному и спроектированы с учетом различных компромиссов, что приводит к различиям в поведении этих систем.
Если независимость от СУБД для вас очень важна, вам нужно хорошо ощущать, где находятся слабые места в каждом сервере. MySQL - очень быстрый сервер, если речь идет о выборках/вставках, но у нас все еще есть проблемы, когда с одной таблицей в смешанном режиме работают медленные клиенты, делающие выборки и обновления. С другой стороны, при работе в Oracle возникают большие проблемы, когда вы хотите получить доступ к строке, которую только что обновили (до тех пор, пока она не будет сохранена на диске). Транзакционные базы данных обычно не очень подходят для генерации отчетов по файлам журналов, так как в этом случае блокировки совершенно бесполезны.
Чтобы сделать свое приложение действительно не зависящим от СУБД, вам следует создать некий быстро расширяемый интерфейс, через который происходит обработка данных. Поскольку C++ доступен на большинстве систем, имеет смысл создать соответствующие классы-интерфейсы к базам данных.
Если вы используете некоторые
специфические функции СУБД
(скажем, REPLACE в MySQL), вам
следует написать код, реализующий
этот метод для других серверов SQL. С
MySQL вы можете использовать такой
синтаксис для того, чтобы добавить
некоторые специфические для MySQL
ключевые слова в запрос: /*!
*/. Код внутри /* */
будет проигнорирован как
комментарий большинством других
SQL-серверов.
Если скорость важнее точности данных, как в некоторых веб-приложениях, то тогда можно создать промежуточный уровень, который кэширует запросы и таким образом дает еще больший выигрыш по скорости. Убирая некоторые запросы из кэша по истечении времени, вы можете держать кэш в достаточно "свежем" состоянии. Таким образом можно избежать пиков повышения нагрузки на сервер, т.к. вы можете динамически увеличить кэш и продолжительность жизни информации, и сохранять эти параметры таковыми, пока ситуация не стабилизируется.
В этом случае структура таблицы должна содержать информацию об изначальном размере кэша и то, как часто таблица должна быть обновлена в общем случае.
5.1.3. Для чего мы использовали MySQL?
На первых этапах развития MySQL его функциональные возможности разрабатывались под потребности самого крупного из наших заказчиков. Это делалось для обслуживания больших хранилищ данных для пары самых крупных продавцов в Швеции.
По всем магазинам мы получаем еженедельный отчет по продажам по бонусным карточкам, и обеспечиваем владельцам магазинов полезной информацией о том, как рекламные компании влияют на их покупателей.
Объем этих данных весьма значителен (в сумме приблизительно 7 миллионов транзакций в месяц), и, кроме того, мы должны предоставлять пользователям данные за периоды от 4 до 10 лет. Каждую неделю мы получаем от клиентов просьбы предоставить "мгновенный" доступ к новым отчетам на основе этих данных.
Эта проблема была решена следующим
образом. Мы сохраняем всю
информацию за месяц в сжатых
таблицах "транзакций". У нас есть
набор простых макросов (сценарий),
генерирующий итоговые таблицы,
сгруппированные по различным
критериям (группа изделий,
идентификатор заказчика,
хранилище...) из таблиц транзакций.
Отчеты - это веб-страницы,
динамически генерирующиеся
небольшим сценарием на Perl, который
просматривает веб-страницу,
выполняет SQL-операторы,
содержащиеся в ней и вставляет
результаты. Для этих целей можно
было бы использовать PHP или модуль
mod_perl, но в то время этих
средств еще не существовало.
Для графических данных мы написали простой инструмент на C, который может создавать GIF-файлы на основе результата SQL-запроса (определенным образом обработав результат). Это также динамически выполняется из создаваемой Perl'ом странички.
В большинстве случаев новый отчет может быть создан просто путем копирования существующего сценария и модифицирования SQL-запроса в нем. Иногда требуется дополнительно добавить поля в существующую итоговую таблицу или сгенерировать новую таблицу, но это также делается очень просто, поскольку у нас все транзакционные таблицы хранятся на диске (в настоящее время у нас имеется меньшей мере 50Гб транзакционных таблиц и 200Гб других клиентских данных.)
Кроме того, мы обеспечиваем для наших клиентов возможность обращаться к итоговым таблицам непосредственно через интерфейс ODBC; таким образом, продвинутые пользователи могут самостоятельно экспериментировать с данными.
У нас не было каких-либо проблем при обработке этих данных на весьма скромном Sun Ultra SPARCstation (2x200 МГц). Недавно мы заменили один из наших серверов на двухпроцессорный UltraSPARC с тактовой частотой 400 МГц и теперь планируем начать обрабатывать транзакции на уровне продукта, что будет означать десятикратное увеличение объема данных. Мы полагаем, что сможем справиться с этим объемом лишь только добавлением соответствующего количества дисков.
Помимо этого мы экспериментируем с Intel-Linux, чтобы получить больше производительности по низшей цене. Теперь, имея бинарно-переносимый формат базы данных (появившийся в версии 3.23), мы начнем использовать его для некоторых частей приложения.
Наша интуиция подсказывает, что у Linux производительность значительно выше при низкой и средней загрузке, а у Solaris - когда высокая загрузка начнет возникать из-за критического дискового ввода-вывода. Но у нас нет пока никаких выводов по этому поводу. После обсуждения с разработчиками ядра Linux мы выяснили, что в это может быть побочным эффектом работы ядра: когда Linux дает слишком много ресурсов пакетным заданиям, задачи взаимодействия начинают замедляться. Из-за этого машина работает очень медленно и не реагирует ни на что, пока обрабатываются большие пакеты. Надеемся, что в последующих ядрах Linux этот вопрос найдет свое решение.
5.1.4. Набор тестов MySQL (The MySQL Benchmark Suite)
В данном разделе будет находиться
техническое описание набора
эталонных тестов MySQL (и
crash-me), но оно пока еще не
написано. В настоящее время можно
получить хорошее представление об
эталонном тесте, глядя на код и
результаты в каталоге
sql-bench любого исходного
дистрибутива MySQL.
Данный набор эталонных создан с целью обеспечить эталонный тест, который будет информировать любого пользователя о том, что в данной реализации SQL выполняется хорошо, а что плохо.
Обратите внимание: этот эталонный тест - однопоточный, так что в нем измеряется минимальное время выполнения операций. В будущем мы планируем добавить в данный набор большое количество многопоточных тестов.
Например (выполнено на одной машине под NT 4.0):
| Чтение 2000000 строк по индексу | Секунды | Секунды |
| mysql | 367 | 249 |
| mysql_odbc | 464 | |
| db2_odbc | 1206 | |
| informix_odbc | 121126 | |
| ms-sql_odbc | 1634 | |
| oracle_odbc | 20800 | |
| solid_odbc | 877 | |
| sybase_odbc | 17614 |
| Вставка 350768 строк | Секунды | Секунды |
| mysql | 381 | 206 |
| mysql_odbc | 619 | |
| db2_odbc | 3460 | |
| informix_odbc | 2692 | |
| ms-sql_odbc | 4012 | |
| oracle_odbc | 11291 | |
| solid_odbc | 1801 | |
| sybase_odbc | 4802 |
В предыдущем тесте MySQL запускался с 8-мегабайтным индексным кэшем.
Гораздо больше результатов тестов вы сможете найти по адресу http://www.mysql.com/information/benchmarks.html.
Обратите внимание: данные об Oracle отсутствуют - по просьбе компании Oracle вся информация по их продукту была удалена. Все эталонные тесты для Oracle должны быть пропущены через компанию Oracle! Мы считаем, однако, что при таком способе тестирования результаты эталонных тестов для Oracle будут в очень высокой степени различаться, поскольку приведенные на сайте результаты призваны показывать на что способна стандартная инсталляция для одного клиента.
Чтобы выполнить набор эталонных тестов, необходимо загрузить исходный дистрибутив MySQL, установить драйвер perl DBI, драйвер perl DBD для той базы данных, которую нужно проверить, а затем выполнить:
cd sql-bench perl run-all-tests --server=#
где # - один из поддерживаемых
серверов. Список всех опций и
поддерживаемых серверов можно
получить, выполнив run-all-tests
--help.
Программа crash-me пытается
определить, какие функции
поддерживаются СУБД, и какие
возможности и ограничения имеют
эти функции при выполнении
запросов. Например, она определяет
следующее:
какие типы столбцов поддерживаются
сколько индексов поддерживается
какие функции поддерживаются
насколько большим может быть запрос
насколько большим может быть столбец
VARCHAR
5.1.5. Использование собственных тестов
Чтобы найти "узкие места" в своем приложении и базе данных, вы должны их тщательно протестировать. После устранения "узкого места" (или после замены его некой заглушкой) можно легко идентифицировать следующее "узкое место" (и так далее). Даже если общая производительность приложения достаточна, нужно по крайней мере выявить все "узкие места" и определиться с тем, как их устранять, - на будущее, если когда-нибудь потребуется дополнительная производительность.
Примеры переносимых программ программы для эталонного тестирования можно найти в наборе тестов MySQL. See Раздел 5.1.4, «Набор тестов MySQL (The MySQL Benchmark Suite)». Можно взять любую программу из этого набора и модифицировать ее для своих потребностей. Таким образом можно испытывать различные решения проблемы и проверять, которое из них самое быстрое.
Зачастую некоторые проблемы проявляются только тогда, когда система очень сильно загружена. К нам часто обращаются клиенты, которые, запустив (протестированную) систему в производство, сталкиваются с проблемами, связанными с нагрузкой. На сегодня причиной каждого из этих случаев были либо проблемы, связанные с базовой конструкцией (при высокой нагрузке нехорошо выполняется сканирование таблиц), либо проблемы ОС/библиотек. И большинство таких проблем было бы намного легче устранить до начала промышленной эксплуатации систем.
Чтобы избежать подобных проблем,
нужно постараться выполнить
эталонное тестирование всего
приложения при самой плохой
возможной нагрузке! Для этого
можно использовать программу
Super Smack, которая доступна
по адресу:
http://www.mysql.com/Downloads/super-smack/super-smack-1.0.tar.gz.
Эта программа, как следует из ее
имени (smack - шлепок - прим.
пер.), способна поставить
систему на колени, так что
используйте её только на
разрабатываемых системах (проще
говоря, в девелопменте).
5.2. Оптимизация SELECT и других запросов
- 5.2.1. Синтаксис оператора
EXPLAIN(получение информации оSELECT) - 5.2.2. Оценка производительности запроса
- 5.2.3. Скорость выполнения запросов
SELECT - 5.2.4. Как MySQL оптимизирует выражения
WHERE - 5.2.5. Как MySQL оптимизирует
DISTINCT - 5.2.6. Как MySQL оптимизирует
LEFT JOINиRIGHT JOIN - 5.2.7. Как MySQL оптимизирует
ORDER BY - 5.2.8. Как MySQL оптимизирует
LIMIT - 5.2.9. Скорость выполнения запросов
INSERT - 5.2.10. Скорость выполнения запросов
UPDATE - 5.2.11. Скорость выполнения запросов
DELETE - 5.2.12. Другие советы по оптимизации
Сначала приведем одно правило, касающееся всех запросов: Чем сложнее ваша система привилений, тем больше издержек.
Если не было выполнено никаких
операторов GRANT, MySQL
каким-то образом будет
оптимизировать проверку
полномочий. Таким образом при
наличии очень большого объема
данных лучше, наверное, будет
работать без привилегий. В
противном случае при большом
количестве полномочий проверка
результатов будет происходить с
увеличенными издержками.
Если проблема состоит в некоторой явной функции MySQL, всегда можно протестировать ее в клиенте:
mysql> SELECT BENCHMARK(1000000,1+1);
+------------------------+
| BENCHMARK(1000000,1+1) |
+------------------------+
| 0 |
+------------------------+
1 row in set (0.32 sec)
Из приведенного выше примера видно, что MySQL может выполнять более 1000000 операций сложения за 0,32 секунды на PentiumII 400MHz.
Все функции MySQL достаточно хорошо
оптимизированы, но могут попадаться
некоторые исключения, и функция
BENCHMARK(число_циклов,выражение)
- хороший инструмент для выяснения,
присутствует ли проблема в запросе.
5.2.1. Синтаксис оператора EXPLAIN (получение информации о SELECT)
EXPLAIN имя_таблицы или EXPLAIN SELECT опции_выборки
EXPLAIN имя_таблицы
является синонимом операторов
DESCRIBE имя_таблицы и
SHOW COLUMNS FROM имя_таблицы.
Если оператор SELECT
предваряется ключевым словом
EXPLAIN, MySQL сообщит о том,
как будет производиться обработка
SELECT, и предоставит
информацию о порядке и методе
связывания таблиц.
При помощи EXPLAIN можно
выяснить, когда стоит снабдить
таблицы индексами, чтобы получить
более быструю выборку,
использующую индексы для поиска
записей.
Вы должны почаще запускать
ANALYZE TABLE с тем, чтобы
обновлялась статистика по
таблицам, такая как кардинальность
ключей, которые могут повлиять на
выбор оптимизатора. See
Раздел 4.5.2, «Синтаксис команды ANALYZE TABLE ».
Можно проверить, насколько удачный
порядок связывания таблиц был
выбран оптимизатором. Заставить
оптимизатор связывать таблицы в
заданном порядке можно при помощи
указания STRAIGHT_JOIN.
Для непростых соединений
EXPLAIN возвращает строку
информации о каждой из
использованных в работе оператора
SELECT таблиц. Таблицы
перечисляются в том порядке, в
котором они будут считываться. MySQL
выполняет все связывания за один
проход (метод называется "single-sweep
multi-join"). Делается это так: MySQL читает
строку из первой таблицы, находит
совпадающую строку во второй
таблице, затем - в третьей, и так
далее. Когда обработка всех таблиц
завершается, MySQL выдает выбранные
столбцы и обходит в обратном
порядке список таблиц до тех пор,
пока не будет найдена таблица с
наибольшим совпадением строк.
Следующая строка считывается из
этой таблицы и процесс
продолжается в следующей таблице.
В MySQL 4.1, вывод EXPLAIN был
изменен с тем, чтобы работать лучше
с конструкциями типа UNION,
подзапросами, и наследованными
(вторичными, derived) таблицами.
Наиболее заметным изменением
стало введение двух новых
столбцов: id и
select_type.
Вывод команды EXPLAIN
включает следующие столбцы:
idИдентификатор
SELECT, последовательный номер этого конкретногоSELECTв запросе.select_typeТип оператора
SELECT, который может быть один из следующих:SIMPLEПростая выборка (
SELECTбезUNIONили подзапросов).PRIMARYКрайний
SELECT.UNIONВторой и дальнейшие
UNION SELECT.DEPENDENT UNIONВторой и дальнейшие
UNION SELECT, зависящие от внешнего подзапроса.SUBSELECTПервый
SELECTв подзапросе.DEPENDENT SUBSELECTПервый
SELECT, зависящий от внешнего подзапроса.DERIVEDНаследованная (вторичная) таблица
SELECT.
tableТаблица, к которой относится выводимая строка.
typeТип связывания. Ниже перечислены различные типы связывания, упорядоченные от лучшего к худшему:
systemТаблица содержит только одну строку (= системная таблица). Это - частный случай типа связывания
const.constТаблица содержит не более одной соответствующей строки, которая будет считываться в начале запроса. Поскольку имеется только одна строка, оптимизатор в дальнейшем может расценивать значения этой строки в столбце как константы. Таблицы
constявляются очень быстрыми, поскольку они читаются только однажды!eq_refДля каждой комбинации строк из предыдущих таблиц будет cчитываться одна строка из этой таблицы. Это наилучший возможный тип связывания среди типов, отличных от
const. Данный тип применяется, когда все части индекса используются для связывания, а сам индекс -UNIQUEилиPRIMARY KEY.refИз этой таблицы будут считываться все строки с совпадающими значениями индексов для каждой комбинации строк из предыдущих таблиц. Тип
refприменяется, если для связывания используется только крайний левый префикс ключа, или если ключ не являетсяUNIQUEилиPRIMARY KEY(другими словами, если на основании значения ключа для связывания не может быть выбрана одна строка). Этот тип связывания хорошо работает, если используемый ключ соответствует только нескольким строкам.rangeПри помощи индекса для выборки строк будут извлечены только строки, находящиеся в заданном диапазоне. Используемый индекс указывается в столбце
key. Столбецkey_lenсодержит самую длинную часть ключа, которая была использована. Столбецrefбудет содержать значенияNULLдля этого типа.indexДанный тип аналогичен
ALL, за исключением того, что просматривается только дерево индексов. Этот тип обычно более быстрый чемALL, поскольку индексный файл, как правило, меньше файла данных.ALLДля каждой комбинации строк из предыдущих таблиц будет производиться полный просмотр этой таблицы. Это обычно плохо, если таблица - первая из не отмеченных как
const, и очень плохо во всех остальных случаях. Как правило, можно избегать типа связыванияALL- путем добавления большего количества индексов таким образом, чтобы строка могла быть найдена при помощи константных значений или значений столбца из предыдущих таблиц.
possible_keysСтолбец
possible_keysслужит для указания индексов, которые может использовать MySQL для нахождения строк в этой таблице. Обратите внимание: этот столбец полностью независим от порядка таблиц. Это означает, что на практике некоторые ключи в столбцеpossible_keysмогут не годиться для сгенерированного порядка таблиц.Если данный столбец пуст, то никаких подходящих индексов не имеется. В этом случае для увеличения производительности следует исследовать выражение
WHERE, чтобы увидеть, есть ли в нем ссылки на какой-либо столбец (столбцы), которые подходили бы для индексации. Если да, создайте соответствующий индекс и снова проверьте запрос при помощи оператораEXPLAIN. See Раздел 6.5.4, «Синтаксис оператораALTER TABLE».Чтобы увидеть, какие индексы есть в таблице, используйте команду
SHOW INDEX FROM имя_таблицы.keyСтолбец
keyсодержит ключ (индекс), который MySQL решил использовать в действительности. Если никакой индекс не был выбран, ключ будет иметь значениеNULL. Чтобы заставить MySQL применить индекс изpossible_keys, следует использовать операторUSE INDEX/IGNORE INDEXв запросе. See Раздел 6.4.1, «Синтаксис оператораSELECT».Также, выполнение
myisamchk --analyze(see Раздел 4.4.6.1, «Синтаксис запускаmyisamchk») илиANALYZE TABLE(see Раздел 4.5.2, «Синтаксис командыANALYZE TABLE») по таблицам даст возможность оптимизатору принимать более правильные решения.key_lenСтолбец
key_lenсодержит длину ключа, которую решил использовать MySQL. Еслиkeyимеет значениеNULL, то длина ключа (key_len) тожеNULL. Обратите внимание: по значению длины ключа можно определить, сколько частей составного ключа в действительности будет использовать MySQL.refСтолбец
refпоказывает, какие столбцы или константы используются с ключом, указанным вkey, для выборки строк из таблицы.rowsВ столбце
rowsуказывается число строк, которые MySQL считает нужным проанализировать для выполнения запроса.ExtraЭтот столбец содержит дополнительную информацию о том, как MySQL будет выполнять запрос. Ниже объясняется значение каждой из текстовых строк, которые могут находиться в этом столбце:
DistinctПосле нахождения первой совпадающей строки MySQL не будет продолжать поиск строк для текущей комбинации строк.
Not existsMySQL смог осуществить оптимизацию
LEFT JOINдля запроса и после нахождения одной строки, соответствующей критериюLEFT JOIN, не будет искать в этой таблице последующие строки для предыдущей комбинации строк.Например:
SELECT * FROM t1 LEFT JOIN t2 ON t1.id=t2.id WHERE t2.id IS NULL;
Предположим, что столбец
t2.idопределен какNOT NULL. В этом случае MySQL просмотрит таблицуt1и будет искать строки вt2соответствующиеt1.id. Если MySQL находит вt2нужную строку, он знает, чтоt2.idникогда не может иметь значениеNULL, и не будет искать вt2оставшуюся часть строк, имеющих тот же самыйid. Другими словами, для каждой строки вt1MySQL должен выполнить только один поиск вt2, независимо от того, сколько совпадающих строк содержится вt2.range checked for each record (index map: #)MySQL не нашел достаточно хорошего индекса для использования. Вместо этого для каждой комбинации строк в предшествующих таблицах он будет проверять, какой индекс следует использовать (если есть какой-либо индекс), и применять его для поиска строк в таблице. Это делается не очень быстро, но таким образом таблицы связываются быстрее, чем без индекса.
Using filesortMySQL должен будет сделать дополнительный проход, чтобы выяснить, как извлечь строки в порядке сортировки. Для выполнения сортировки выполняется просмотр всех строк согласно типу связывания (
join type) и сохраняются ключ сортировки плюс указатель на строку для всех строк, удовлетворяющих выражениюWHERE. После этого ключи сортируются и строки извлекаются в порядке сортировки.Using indexДля извлечения данных из столбца используется только информация дерева индексов; при этом нет необходимости производить собственно чтение записи. Это применимо для случаев, когда все используемые столбцы таблицы являются частью одного индекса.
Using temporaryЧтобы выполнить запрос, MySQL должен будет создать временную таблицу для хранения результата. Это обычно происходит, если предложение
ORDER BYвыполняется для набора столбцов, отличного от того, который используется в предложенииGROUP BY.Using whereВыражение
WHEREбудет использоваться для выделения тех строк, которые будут сопоставляться со следующей таблицей или тех, которые будут посланы клиенту. Если этой информации нет, а таблица имеет типALLилиindex, то, значит, в вашем запросе есть какая-то ошибка (если вы не собираетесь делать выборку/тестирование всех строк таблицы).
Если нужно, чтобы запросы выполнялись настолько быстро, насколько это возможно, посмотрите, есть ли строки упоминания
Using filesortиUsing temporary.
Существует неплохой способ
определить, насколько хорошим
является тип связывания. Для этого
нужно перемножить все значения
столбца rows, выводимого
командой EXPLAIN.
Результатом будет грубая оценка
того, сколько строк должен
просмотреть MySQL для выполнения
запроса. Это же число используется
для ограничения запросов в
переменной max_join_size. See
Раздел 5.5.2, «Настройка параметров сервера».
В следующем примере показано, как
можно постепенно оптимизировать
JOIN при помощи
информации, выводимой оператором
EXPLAIN.
Предположим, что имеется
представленный ниже оператор
SELECT, который нужно
исследовать при помощи команды
EXPLAIN:
EXPLAIN SELECT tt.TicketNumber, tt.TimeIn,
tt.ProjectReference, tt.EstimatedShipDate,
tt.ActualShipDate, tt.ClientID,
tt.ServiceCodes, tt.RepetitiveID,
tt.CurrentProcess, tt.CurrentDPPerson,
tt.RecordVolume, tt.DPPrinted, et.COUNTRY,
et_1.COUNTRY, do.CUSTNAME
FROM tt, et, et AS et_1, do
WHERE tt.SubmitTime IS NULL
AND tt.ActualPC = et.EMPLOYID
AND tt.AssignedPC = et_1.EMPLOYID
AND tt.ClientID = do.CUSTNMBR;
Для этого примера принимается, что:
Сравниваемые столбцы были объявлены следующим образом:
Таблица Столбец Тип столбца ttActualPCCHAR(10)ttAssignedPCCHAR(10)ttClientIDCHAR(10)etEMPLOYIDCHAR(15)doCUSTNMBRCHAR(15)Таблицы проиндексированы следующим образом:
Таблица Индекс ttActualPCttAssignedPCttClientIDetEMPLOYID(primary key)doCUSTNMBR(primary key)Значения
tt.ActualPCраспределены не равномерно.
На начальном этапе перед
выполнением какой-либо
оптимизации оператор
EXPLAIN выведет следующую
информацию:
table type possible_keys key key_len ref rows Extra
et ALL PRIMARY NULL NULL NULL 74
do ALL PRIMARY NULL NULL NULL 2135
et_1 ALL PRIMARY NULL NULL NULL 74
tt ALL AssignedPC,ClientID,ActualPC NULL NULL NULL 3872
range checked for each record (key map: 35)
Поскольку каждая таблица имеет тип
(type) ALL, из
приведенного выше вывода видно,
что MySQL будет делать полное
связывание всех таблиц! Это займет
долгое время, поскольку для
выполнения такого связывания
должно быть рассмотрено
произведение числа строк в каждой
таблице! Для нашего случая такое
произведение - 74 * 2135 * 74 * 3872 = 45268558720
строк. Если таблицы большие, трудно
даже представить себе, как долго
они будут связываться.
Одна проблема здесь состоит в том,
что MySQL не может (пока еще)
эффективно применять индексы к
столбцам, если они объявлены
по-разному. В этом контексте тип
VARCHAR и тип CHAR -
одинаковы, если они не объявлены с
различной длиной. Поскольку
столбец tt.ActualPC объявлен
как CHAR(10), а
et.EMPLOYID - как
CHAR(15), имеется
несоответствие по длине значений.
Чтобы устранить это
несоответствие между длинами
столбцов, следует использовать
команду ALTER TABLE для
удлинения столбца ActualPC
от 10 символов до 15 символов:
mysql> ALTER TABLE tt MODIFY ActualPC VARCHAR(15);
Теперь оба столбца
tt.ActualPC и et.EMPLOYID
имеют тип VARCHAR(15). При
повторном выполнении оператора
EXPLAIN будет выведен
следующий результат:
table type possible_keys key key_len ref rows Extra
tt ALL AssignedPC,ClientID,ActualPC NULL NULL NULL 3872 Using where
do ALL PRIMARY NULL NULL NULL 2135
range checked for each record (key map: 1)
et_1 ALL PRIMARY NULL NULL NULL 74
range checked for each record (key map: 1)
et eq_ref PRIMARY PRIMARY 15 tt.ActualPC 1
Это не идеально, но уже намного
лучше (произведение значений строк
(rows) теперь уменьшилось в
74 раза). Такое связывание
выполнится за пару секунд.
Можно сделать еще одно изменение -
чтобы устранить несоответствие
длин столбцов для сравнений
tt.AssignedPC = et_1.EMPLOYID и
tt.ClientID = do.CUSTNMBR.
mysql>ALTER TABLE tt MODIFY AssignedPC VARCHAR(15),->MODIFY ClientID VARCHAR(15);
Теперь оператор EXPLAIN
будет выводить такую информацию:
table type possible_keys key key_len ref rows Extra
et ALL PRIMARY NULL NULL NULL 74
tt ref AssignedPC, ActualPC 15 et.EMPLOYID 52 Using where
ClientID,
ActualPC
et_1 eq_ref PRIMARY PRIMARY 15 tt.AssignedPC 1
do eq_ref PRIMARY PRIMARY 15 tt.ClientID 1
Это почти идеально.
Осталась еще одна проблема. Она
заключается в том, что по умолчанию
MySQL принимает, что значения в
столбце tt.ActualPC
распределены равномерно, но в
таблице tt это не так. К
счастью, проинформировать MySQL об
этом можно очень просто:
shell>myisamchk --analyze PATH_TO_MYSQL_DATABASE/ttshell>mysqladmin refresh
Теперь связывание совершенно, и
оператор EXPLAIN выведет
такой результат:
table type possible_keys key key_len ref rows Extra
tt ALL AssignedPC NULL NULL NULL 3872 Using where
ClientID,
ActualPC
et eq_ref PRIMARY PRIMARY 15 tt.ActualPC 1
et_1 eq_ref PRIMARY PRIMARY 15 tt.AssignedPC 1
do eq_ref PRIMARY PRIMARY 15 tt.ClientID 1
Обратите внимание: столбец rows в
выводе оператора EXPLAIN -
опытное предположение
оптимизатора связей MySQL. Чтобы
оптимизировать запрос, нужно
проверить, являются ли числа
близкими к действительным. Если
нет, можно получить лучшую
производительность, используя в
операторе SELECT
соединение STRAIGHT_JOIN и
попытаться задать другой порядок
таблиц в выражении FROM.
5.2.2. Оценка производительности запроса
В большинстве случаев можно оценивать производительность путем подсчета дисковых операций. Для маленьких таблиц можно обычно принимать 1 строку за 1 операцию дискового поиска (поскольку индекс, скорее всего, в кэше). Для больших таблиц можно считать, что (при использовании индексов типа B++ деревьев) для нахождения строки потребуется
log(количество_строк) /
log(длина_индексного_блока / 3 * 2 /
(длина_индекса +
длина_указателя_на_данные)) + 1
дисковая операция для получения строки.
Обычно в MySQL индексный блок
занимает 1024 байта, а указательн - 4
байта. Для таблицы, содержащей 500000
строк и имеющей длину индекса 3
(medium integer) потребуется
log(500,000)/log(1024/3*2/(3+4)) + 1 = 4
дисковых операции поиска.
Поскольку вышеупомянутый индекс будет занимать приблизительно 500000 * 7 * 3/2 = 5,2Mб (если учитывать, что индексные буфера обычно заполняются на 2/3), большая часть индекса, скорее всего, окажется в памяти, и для того, чтобы найти строку, потребуется лишь 1-2 обращения к ОС для чтения.
Для записи, однако, потребуется 4 дисковых запроса (таких, какие рассматривались выше) чтобы найти место для помещения нового индекса, и обычно 2 дисковых операции, чтобы обновить индекс и вставить строку.
Обратите внимание: сказанное выше
не означает, что
производительность приложения
будет ухудшаться в log N
раз! Поскольку все кэшируется в OС
или на SQL-сервере, замедление
работы при увеличении таблицы
будет незначительным. И лишь после
того, как данных станет так много,
что они перестанут помещаться в
кэш, замедление работы там, где
работа приложения сводится только
к операциям дискового поиска
(количество которых растет в
log N), станет гораздо
ощутимей. Чтобы избежать этого,
следует увеличить индексный кэш
так, чтобы он вмещал возросшее
количество данных. See
Раздел 5.5.2, «Настройка параметров сервера».
5.2.3. Скорость выполнения запросов SELECT
В общем случае для того, чтобы
заставить медленный SELECT ...
WHERE работать быстрее, прежде
всего нужно выяснить, можно ли
добавить индекс. Для всех ссылок
между различными таблицами должны,
как правило, применяться индексы.
Чтобы определить, какие индексы
используются для выборки
SELECT, можно использовать
EXPLAIN. See Раздел 5.2.1, «Синтаксис оператора EXPLAIN (получение информации о SELECT)».
Вот несколько общих советов:
Чтобы MySQL лучше оптимизировал запросы, можно выполнить
myisamchk --analyzeдля таблицы после того, как она загружена соответствующими данными. Таким образом для каждой части индекса будет обновлено значение, указывающее среднее число строк, имеющих одинаковые значения (для уникальных индексов это всегда 1, разумеется). MySQL будет использовать это число, чтобы решить, какой индекс следует выбрать для связывания двух таблиц при помощи "неконстантного выражения". Результат работыanalyzeможно увидеть в столбцеCardinalityпосле выполнения командыSHOW INDEX FROM имя_таблицы.Чтобы отсортировать индекс и данные в соответствии с индексом, используйте
myisamchk --sort-index --sort-records=1(если нужно отсортировать по индексу 1). Если имеется уникальный индекс, по которому вы хотите считывать все записи в порядке, соответствующем данному индексу, это - хороший способ ускорить считывание записей. Обратите внимание, однако, что эта сортировка написана не оптимально и для большой таблицы будет выполняться долго!
5.2.4. Как MySQL оптимизирует выражения WHERE
Описание оптимизации выражений
WHERE помещено в раздел,
посвященный SELECT, потому
что они главным образом
используются в запросах
SELECT, но для выражений
WHERE в операторах
DELETE и UPDATE
используются те же способы
оптимизации.
Отметим также, что данный раздел неполон. В MySQL реализовано много возможностей оптимизации, и у нас не было времени, чтобы задокументировать их все.
Ниже перечислены некоторые из оптимизации, выполняемых MySQL:
Удаляются ненужные скобки:
((a AND b) AND c OR (((a AND b) AND (c AND d)))) -> (a AND b AND c) OR (a AND b AND c AND d)
Константы заменяются значениями:
(a<b AND b=c) AND a=5 -> b>5 AND b=c AND a=5
Удаляются условия для констант (требуется при замене констант значением):
(B>=5 AND B=5) OR (B=6 AND 5=5) OR (B=7 AND 5=6) -> B=5 OR B=6
Константные выражения, используемые индексами, оцениваются только один раз.
Для таблиц
HEAPиMyISAMфункцияCOUNT(*), которая вызывается для одной таблицы и не содержит предложенияWHERE, берется непосредственно из табличной информации. Это делается также для любого выраженияNOTNULL, в котором используется только одна таблица.Недопустимые константные выражения выявляются на ранних этапах. MySQL быстро обнаруживает, что некоторые операторы
SELECTнеосуществимы и не возвращают строк.Выполняется слияние выражения
HAVINGсWHERE, если не используется предложениеGROUP BYили групповые функции (COUNT(), MIN()...).Для каждого подчиненного связывания создается более простое предложение
WHERE, чтобы ускорить оценкуWHEREдля каждого подчиненного связывания а также чтобы пропустить записи как можно быстрее.Все константные таблицы считываются в первую очередь, перед любыми другими таблицами в запросе. К константным таблицам относятся следующие:
Пустая таблица или таблица с 1 строкой.
Таблица, которая используется с выражением
WHEREдля индексаUNIQUE, илиPRIMARY KEY, где все части индекса используются с константными выражениями и части индекса определены какNOT NULL.
Все эти таблицы используются как константные таблицы:
mysql>
SELECT * FROM t WHERE primary_key=1;mysql>SELECT * FROM t1,t2->WHERE t1.primary_key=1 AND t2.primary_key=t1.id;Лучшая комбинацию связывания для связывания таблиц находится путем испытания всех возможных вариантов. Если все столбцы в предложениях
ORDER BYиGROUP BYпринадлежат одной таблице, эта таблица рассматривается первой при связывании.Если имеется выражение
ORDER BYи отличное от него выражениеGROUP BY, или если выраженияORDER BYилиGROUP BYсодержат столбцы не только из первой таблицы в очереди на связывание, но и из других таблиц, то тогда создается временная таблица.Если используется
SQL_SMALL_RESULT, MySQL будет применять временную таблицу, которую разместит в памяти.Запрашивается каждый индекс таблицы, и используется лучший, охватывающий менее 30% строк. Если такой индекс найти нельзя, используется быстрое сканирование таблицы.
В некоторых случаях MySQL может читать данные из индекса даже без обращения к файлу данных. Если все столбцы, используемые в индексе, числовые, то для выполнения запроса будет использоваться только индексное дерево.
Перед выводом каждой записи пропускаются те, которые не соответствуют выражению
HAVING.
Вот некоторые примеры очень быстрых запросов:
mysql>SELECT COUNT(*) FROM tbl_name;mysql>SELECT MIN(key_part1),MAX(key_part1) FROM tbl_name;mysql>SELECT MAX(key_part2) FROM tbl_name->WHERE key_part_1=constant;mysql>SELECT ... FROM tbl_name->ORDER BY key_part1,key_part2,... LIMIT 10;mysql>SELECT ... FROM tbl_name->ORDER BY key_part1 DESC,key_part2 DESC,... LIMIT 10;
Для выполнения следующих запросов используется только индексное дерево (предполагается, что индексированные столбцы числовые):
mysql>SELECT key_part1,key_part2 FROM tbl_name WHERE key_part1=val;mysql>SELECT COUNT(*) FROM tbl_name->WHERE key_part1=val1 AND key_part2=val2;mysql>SELECT key_part2 FROM tbl_name GROUP BY key_part1;
Следующие запросы используют индексацию, чтобы получить отсортированные строки без дополнительного прохода для сортировки:
mysql>SELECT ... FROM tbl_name->ORDER BY key_part1,key_part2,... ;mysql>SELECT ... FROM tbl_name->ORDER BY key_part1 DESC,key_part2 DESC,... ;
5.2.5. Как MySQL оптимизирует DISTINCT
DISTINCT преобразовывается
к GROUP BY для всех столбцов,
для DISTINCT в сочетании с
ORDER BY, помимо этого, во
многих случаях также требуется
временная таблица.
Если LIMIT # указывается
совместно с DISTINCT, MySQL
остановится, как только найдет #
уникальных строк.
Если не все столбцы и не во всех таблицах используются, MySQL прекратит сканирование неиспользуемых таблиц, как только найдет первое совпадение.
SELECT DISTINCT t1.a FROM t1,t2 where t1.a=t2.a;
В случае, если, предположим,
таблица t1 используется
перед t2 (это проверяется
при помощи EXPLAIN), MySQL
прекратит чтение в t2 (для
каждой отдельной строки из
t1), после того как найдет
первую строку в t2.
5.2.6. Как MySQL оптимизирует LEFT JOIN и RIGHT JOIN
Выражение "A LEFT JOIN B" в MySQL
реализовано следующим образом:
Таблица B устанавливается как зависимая от таблицы A и от всех таблиц, от которых зависит A.
Таблица A устанавливается как зависимая ото всех таблиц (кроме B), которые используются в условии
LEFT JOIN.Все условия
LEFT JOINперемещаются в предложениеWHERE.Выполняются все стандартные способы оптимизации соединения, за исключением того, что таблица всегда читается после всех таблиц, от которых она зависит. Если имеется циклическая зависимость, MySQL выдаст ошибку.
Выполняются все стандартные способы оптимизации
WHERE.Если в таблице A имеется строка, соответствующая выражению
WHERE, но в таблице B ни одна строка не удовлетворяет условиюLEFT JOIN, генерируется дополнительная строка B, в которой все значения столбцов устанавливаются вNULL.Если
LEFT JOINиспользуется для поиска тех строк, которые отсутствуют в некоторой таблице, и в предложенииWHEREвыполняется следующая проверка:column_name IS NULL, гдеcolumn_name- столбец, который объявлен какNOT NULL, MySQL пререстанет искать строки (для отдельной комбинации ключа) после того, как найдет строку, соответствующую условиюLEFT JOIN.
RIGHT JOIN реализован
аналогично LEFT JOIN.
При указании жесткого порядка
чтения таблиц в LEFT JOIN и
STRAIGHT JOIN оптимизатор
связей (который определяет, в каком
порядке таблицы должны быть
связаны) будет выполнять работу
намного быстрее, так как ему
потребуется проверять меньшее
количество перестановок таблиц.
Обратите внимание: отсюда следует, что если выполняется запрос типа
SELECT * FROM a,b LEFT JOIN c ON (c.key=a.key) LEFT JOIN d (d.key=a.key)
WHERE b.key=d.key
MySQL будет делать полный просмотр
таблицы b, поскольку
LEFT JOIN заставит его
читать эту таблицу перед
d.
В этом случае, чтобы предотвратить
полный просмотр таблицы
b, нужно изменить запрос
таким образом:
SELECT * FROM b,a LEFT JOIN c ON (c.key=a.key) LEFT JOIN d (d.key=a.key)
WHERE b.key=d.key
5.2.7. Как MySQL оптимизирует ORDER BY
В некоторых случаях MySQL может
использовать индекс, чтобы
выполнить запрос ORDER BY
или GROUP BY без выполнения
дополнительной сортировки.
Индекс может также использоваться
и тогда, когда предложение ORDER
BY не соответствует индексу в
точности, если все неиспользуемые
части индекса и все столбцы, не
указанные в ORDER BY -
константы в выражении
WHERE. Следующие запросы
будут использовать индекс, чтобы
выполнить ORDER BY / GROUP BY.
SELECT * FROM t1 ORDER BY key_part1,key_part2,... SELECT * FROM t1 WHERE key_part1=constant ORDER BY key_part2 SELECT * FROM t1 WHERE key_part1=constant GROUP BY key_part2 SELECT * FROM t1 ORDER BY key_part1 DESC,key_part2 DESC SELECT * FROM t1 WHERE key_part1=1 ORDER BY key_part1 DESC,key_part2 DESC
Ниже приведены некоторые случаи,
когда MySQL не
может использовать индексы,
чтобы выполнить ORDER BY
(обратите внимание, что MySQL тем не
менее будет использовать индексы,
чтобы найти строки,
соответствующие выражению
WHERE):
Сортировка
ORDER BYделается по нескольким ключам:SELECT * FROM t1 ORDER BY key1,key2Сортировка
ORDER BYделается, при использовании непоследовательных частей ключа:SELECT * FROM t1 WHERE key2=constant ORDER BY key_part2Смешиваются
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, относятся не только к первой неконстантной (const) таблице, используемой для выборки строк (это первая таблица в выводеEXPLAIN, в которой не используется константный,const, метод выборки строк).Имеются различные выражения
ORDER BYиGROUP BY.Используемый индекс таблицы имеет такой тип, который не обеспечивает сортированного хранения строк (как индекс
HASHв таблицахHEAP).
В тех случаях, когда MySQL должен сортировать результат, он использует следующий алгоритм:
Считываются все строки согласно ключу или путем сканирования таблицы. Строки, которые не соответствует предложению WHERE, пропускаются.
Ключ сортировки сохраняется в буфере сортировки (размера
sort_buffer)Когда буфер заполняется, содержимое буфера сортируется алгоритмом
qsort, результаты сохраняются во временном файле. Сохраняется указатель на отсортированный блок (в том случае, когда все строки умещаются в буфере сортировки, временный файл не создается).Вышеупомянутое действие повторяется, пока не будут считаны все строки.
Делается мультислияние до
MERGEBUFF(7) областей в один блок в другом временном файле. Это действие повторяется, пока все блоки из первого файла не окажутся во втором файле.Предыдущий пункт повторяется, пока не останется менее
MERGEBUFF2(15) блоков.При последнем мультислиянии в результирующий файл записывается только указатель на строку (последняя часть ключа сортировки).
Теперь код в файле
sql/records.ccбудет использоваться для чтения данных в отсортированном порядке, с использованием указателей на строки из результирующего файла. Чтобы оптимизировать этот процесс, мы считываем большой блок указателей на строки, сортируем их, и затем считываем строки в отсортированном порядке в буфер строк (record_rnd_buffer).
При помощи команды EXPLAIN SELECT ...
ORDER BY можно проверить, может
ли MySQL использовать индексы для
выполнения запроса. Если в столбце
extra содержится значение
Using filesort, то MySQL не может
использовать индексы для
выполнения