предисловие
MySQL — это тип реляционной базы данных.Сильная функция запроса,Высокая согласованность данных,Высокая безопасность данных,Поддержка вторичного индекса. Однако производительность немного уступает нереляционным базам данных, особенно для данных более одного миллиона уровней, которые подвержены медленным запросам. На данный момент необходимо проанализировать причины медленного выполнения запроса, как правило, SQL программиста написана плохо, или нет индекса ключа, или индекс недействителен.
В настоящее время особенно важна команда EXPLAIN, предоставляемая MySQL, которая можетSELECT
Выписка анализируется и выводитсяSELECT
Детали выполнения для целевой оптимизации разработчиками.
И просто добавьте объяснение перед оператором запроса:
EXPLAIN SELECT * FROM customer WHERE id < 100;
Подготовить
Во-первых, вам нужно создать две тестовые таблицы и данные:
CREATE TABLE `customer` (
`id` BIGINT(20) unsigned NOT NULL AUTO_INCREMENT,
`name` VARCHAR(50) NOT NULL DEFAULT '',
`age` INT(11) unsigned DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `name_index` (`name`)
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4
INSERT INTO customer (name, age) VALUES ('a', 1);
INSERT INTO customer (name, age) VALUES ('b', 2);
INSERT INTO customer (name, age) VALUES ('c', 3);
INSERT INTO customer (name, age) VALUES ('d', 4);
INSERT INTO customer (name, age) VALUES ('e', 5);
INSERT INTO customer (name, age) VALUES ('f', 6);
INSERT INTO customer (name, age) VALUES ('g', 7);
INSERT INTO customer (name, age) VALUES ('h', 8);
INSERT INTO customer (name, age) VALUES ('i', 9);
CREATE TABLE `orders` (
`id` BIGINT(20) unsigned NOT NULL AUTO_INCREMENT,
`user_id` BIGINT(20) unsigned NOT NULL DEFAULT 0, `product_name` VARCHAR(50) NOT NULL DEFAULT '',
`productor` VARCHAR(30) NOT NULL DEFAULT '',
PRIMARY KEY (`id`),
KEY `user_product_detail_index` (`user_id`, `product_name`, `productor`)
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4
INSERT INTO orders (user_id, product_name, productor) VALUES (1, 'p1', 'WHH');
INSERT INTO orders (user_id, product_name, productor) VALUES (1, 'p2', 'WL');
INSERT INTO orders (user_id, product_name, productor) VALUES (1, 'p1', 'DX');
INSERT INTO orders (user_id, product_name, productor) VALUES (2, 'p1', 'WHH');
INSERT INTO orders (user_id, product_name, productor) VALUES (2, 'p5', 'WL');
INSERT INTO orders (user_id, product_name, productor) VALUES (3, 'p3', 'MA');
INSERT INTO orders (user_id, product_name, productor) VALUES (4, 'p1', 'WHH');
INSERT INTO orders (user_id, product_name, productor) VALUES (6, 'p1', 'WHH');
INSERT INTO orders (user_id, product_name, productor) VALUES (9, 'p8', 'TE');
EXPLAIN выходной формат
Вывод команды EXPLAIN примерно следующий:
mysql> explain select * from customer where id = 1\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: customer
partitions: NULL
type: const
possible_keys: PRIMARY
key: PRIMARY
key_len: 8
ref: const
rows: 1
filtered: 100.00
Extra: NULL
1 row in set, 1 warning (0.00 sec)
Значения столбцов следующие:
id: идентификатор запроса SELECT. Каждому SELECT автоматически присваивается уникальный идентификатор.
select_type: тип запроса SELECT.
таблица: какая таблица запрашивается
разделы: соответствующие разделы
тип: тип соединения
возможных_ключей: индексы, которые можно использовать в этом запросе.
ключ: точный индекс, используемый в этом запросе.
ref: какое поле или константу использовать с ключом
rows: показывает, сколько строк было просмотрено этим запросом. Это оценка.
filtered: указывает процент данных, отфильтрованных по этому условию запроса.
дополнительно: дополнительная информация
Далее, давайте сосредоточимся на некоторых из наиболее важных полей.
select_type
SIMPLE — простой запрос на выборку, не содержащий подзапросов или UNION.
PRIMARY — если запрос содержит какие-либо сложные подзапросы, самый внешний запрос помечается как основной.
UNION — указывает, что этот запрос является вторым или последующим запросом к UNION.
DEPENDENT UNION — второй или последующий оператор запроса в UNION, в зависимости от внешнего запроса
UNION RESULT - выберите результат, чтобы получить результаты из таблицы UNION.
DERIVED - Подзапросы, содержащиеся в списке from, помечаются как производные. MySQL будет рекурсивно выполнять эти подзапросы, помещая результаты во временную таблицу.
SUBQUERY — содержит подзапрос в списке select или where
ЗАВИСИМЫЙ ПОДЗАПРОС — первый SELECT в подзапросе, который зависит от внешнего запроса, то есть подзапрос зависит от результата внешнего запроса.
Наиболее распространенная категория запросов должна бытьSIMPLE
Например, если в нашем запросе нет ни подзапросов, ни запросов UNION, то обычноSIMPLE
типа, например:
mysql> explain select * from customer where id = 2\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: customer
partitions: NULL
type: const
possible_keys: PRIMARY
key: PRIMARY
key_len: 8
ref: const
rows: 1
filtered: 100.00
Extra: NULL
1 row in set, 1 warning (0.00 sec)
Если бы мы использовали запрос UNION, вывод EXPLAIN выглядел бы так:
mysql> EXPLAIN (SELECT * FROM customer WHERE id IN (1, 2, 3))
-> UNION
-> (SELECT * FROM customer WHERE id IN (3, 4, 5));
+----+--------------+------------+------------+-------+---------------+---------+---------+------+------+----------+-----------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+--------------+------------+------------+-------+---------------+---------+---------+------+------+----------+-----------------+
| 1 | PRIMARY | customer | NULL | range | PRIMARY | PRIMARY | 8 | NULL | 3 | 100.00 | Using where |
| 2 | UNION | customer | NULL | range | PRIMARY | PRIMARY | 8 | NULL | 3 | 100.00 | Using where |
| NULL | UNION RESULT | <union1,2> | NULL | ALL | NULL | NULL | NULL | NULL | NULL | NULL | Using temporary |
+----+--------------+------------+------------+-------+---------------+---------+---------+------+------+----------+-----------------+
3 rows in set, 1 warning (0.00 sec)
table
Указывает таблицу или производную таблицу, задействованную в запросе.
type
type
Поле более важно, оно обеспечивает важную основу для оценки эффективности запроса.type
поле, мы считаем, что этот запрос全表扫描
все еще索引扫描
Ждать.
тип общий тип
Обычно используемые значения для типа:
system
: В таблице только одни данные.Этот тип особенныйconst
тип.const
: Эквивалентное сканирование запроса по первичному ключу или уникальному индексу возвращает не более одной строки данных.Константный запрос очень быстрый, потому что его нужно прочитать только один раз.
Например, в приведенном ниже запросе используется индекс первичного ключа, поэтомуtype
этоconst
Тип.
mysql> explain select * from customer where id = 2\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: customer
partitions: NULL
type: const
possible_keys: PRIMARY
key: PRIMARY
key_len: 8
ref: const
rows: 1
filtered: 100.00
Extra: NULL
1 row in set, 1 warning (0.00 sec)
eq_ref
: этот тип обычно появляется в запросах на соединение нескольких таблиц, указывая, что для каждого результата предыдущей таблицы может быть сопоставлена только одна строка результатов из последней таблицы.И операция сравнения запроса обычно=
, эффективность запросов выше, например:
mysql> EXPLAIN SELECT * FROM customer, order_info WHERE customer.id = order_info.user_id\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: order_info
partitions: NULL
type: index
possible_keys: user_product_detail_index
key: user_product_detail_index
key_len: 314
ref: NULL
rows: 9
filtered: 100.00
Extra: Using where; Using index
*************************** 2. row ***************************
id: 1
select_type: SIMPLE
table: customer
partitions: NULL
type: eq_ref
possible_keys: PRIMARY
key: PRIMARY
key_len: 8
ref: test.order_info.user_id
rows: 1
filtered: 100.00
Extra: NULL
2 rows in set, 1 warning (0.00 sec)
ref
: этот тип обычно появляется в запросах на соединение нескольких таблиц, нацеленных на индексы с неуникальными или не первичными ключами или использующих最左前缀
Запрос индекса правил.
Например, в следующем примере используетсяref
Тип запроса:
mysql> EXPLAIN SELECT * FROM customer, order_info WHERE customer.id = order_info.user_id AND order_info.user_id = 5\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: customer
partitions: NULL
type: const
possible_keys: PRIMARY
key: PRIMARY
key_len: 8
ref: const
rows: 1
filtered: 100.00
Extra: NULL
*************************** 2. row ***************************
id: 1
select_type: SIMPLE
table: order_info
partitions: NULL
type: ref
possible_keys: user_product_detail_index
key: user_product_detail_index
key_len: 9
ref: const
rows: 1
filtered: 100.00
Extra: Using index
2 rows in set, 1 warning (0.01 sec)
range
: указывает, что используется запрос диапазона индекса, и некоторые записи данных в таблице получены через диапазон поля индекса.Этот тип обычно появляется в =, , >, >=, , МЕЖДУ, В() в работе.
когдаtype
даrange
, затем EXPLAIN выводитref
поле равно NULL иkey_len
Это поле с самым длинным индексом, используемым в этом запросе.
Например, следующий пример представляет собой запрос диапазона:
mysql> EXPLAIN SELECT * FROM customer WHERE id BETWEEN 2 AND 8 \G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: customer
partitions: NULL
type: range
possible_keys: PRIMARY
key: PRIMARY
key_len: 8
ref: NULL
rows: 7
filtered: 100.00
Extra: Using where
1 row in set, 1 warning (0.00 sec)
index
: представляет полное сканирование индекса, аналогичное типу ALL, за исключением того, что тип ALL представляет собой полное сканирование таблицы, а тип индекса сканирует только все индексы без сканирования данных.index
Тип обычно возникает, когда: запрашиваемые данные могут быть получены непосредственно в дереве индекса без сканирования данных В этом случае в поле Дополнительно будет отображатьсяUsing index
.
Например:
mysql> EXPLAIN SELECT name FROM customer \G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: customer
partitions: NULL
type: index
possible_keys: NULL
key: name_index
key_len: 152
ref: NULL
rows: 10
filtered: 100.00
Extra: Using index
1 row in set, 1 warning (0.00 sec)
В приведенном выше примере поле имени, которое мы запрашиваем, является индексом, поэтому мы можем напрямую получать данные из индекса для удовлетворения требований запроса, не запрашивая данные в таблице.Поэтому в этом случае значение типа равноindex
, а значение Extra равноUsing index
.
ALL: указывает на полное сканирование таблицы. Этот тип запроса является одним из наименее производительных запросов. Вообще говоря, наш запрос не должен иметь тип запроса ALL, потому что такой запрос содержит большой объем данных. Производительность — это огромная катастрофа. , Если запрос является запросом типа ALL, его обычно можно избежать, добавив индекс в соответствующее поле.
Ниже приведен пример полного сканирования таблицы.Видно, что во время полного сканирования таблицы поля возможных_ключей и ключа имеют значение NULL, что указывает на то, что индекс не используется, а строки очень велики, поэтому общая эффективность запроса очень низкий.
mysql> EXPLAIN SELECT age FROM customer WHERE age = 20 \G*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: customer
partitions: NULL
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 10
filtered: 10.00
Extra: Using where
1 row in set, 1 warning (0.00 sec)
Сравнение производительности типов
Вообще говоря, отношения производительности разных типов выглядят следующим образом:ALL < index < range ~ index_merge < ref < eq_ref < const < system
ALL
Поскольку этот тип представляет собой полное сканирование таблицы, он является самым медленным при тех же условиях запроса.
иindex
Этот тип запроса не является полным сканированием таблицы, но он сканирует все индексы и поэтому немного быстрее, чем тип ALL.
Последние типы используют индексы для запроса данных, поэтому часть или большую часть данных можно отфильтровать, поэтому эффективность запросов относительно высока.
Для программистов, если запрос гарантированно достигнет хотя быrange
уровне или в лучшем случаеref
Это отличный и ответственный программист.
- ALL: (полное сканирование таблицы) Полное сканирование таблицы, несомненно, является наихудшим.Если количество данных составляет миллионы, полное сканирование таблицы будет очень медленным.
- index: (полное сканирование индекса) Полное сканирование файла индекса намного лучше всех, ведь поиск данных из индексного дерева выполняется быстрее, чем поиск данных из полной таблицы.
- range: Получить только заданный диапазон строк, используя индекс для сопоставления строк. Диапазон сужается и, конечно, быстрее, чем полное сканирование таблицы и полное сканирование файлов индекса. Операторы SQL обычно имеют между, в, >,
- ref: Сканирование неуникального индекса, которое по сути является доступом к индексу, возвращает все строки, соответствующие одному значению. Например, при запросе всех коллег, принадлежащих к группе исследований и разработок в компании, совпадающие результаты представляют собой несколько, но не уникальных значений.
- eq_ref: Уникальное сканирование индекса, для каждого ключа индекса есть одна соответствующая ему запись в таблице. Например, если вы запрашиваете генерального директора компании, результатом сопоставления может быть только одна запись.
- const: указывает, что его можно найти по индексу один раз, а const используется для сравнения первичного ключа или уникального индекса. Поскольку сопоставляется только одна строка данных, MySQL может быстро преобразовать запрос в константу, если первичный ключ помещен в список where.
- system: В таблице есть только одна запись (равная системной таблице), представляющая собой специальный столбец типа const, который обычно не отображается, просто поймите
possible_keys
possible_keys
Указывает индексы, которые MySQL может использовать при выполнении запросов. Обратите внимание, что даже некоторые индексыpossible_keys
появляется в , но это не означает, что этот индекс будет фактически использоваться MySQL.Какие индексы используются MySQL при запросе, определяетсяkey
полевое решение.
key
Это поле является индексом, фактически используемым MySQL в текущем запросе.
key_len
Указывает количество байтов индекса, используемого оптимизатором запросов. Это поле можно использовать для оценки того, используется ли составной индекс полностью или используется только крайнее левое поле.
Правила расчета key_len следующие:
-
нить
char(n): длина n байтов
-
varchar(n): 3, если кодировка utf8
n + 2 байта; 4, если кодировка utf8mb4n + 2 байта.
-
Числовой тип:
TINYINT: 1 байт
МАЛЕНЬКИЙ INT: 2 байта
СРЕДНИЙ: 3 байта
INT: 4 байта
БОЛЬШОЙ: 8 байт
-
тип времени
ДАТА: 3 байта
TIMESTAMP: 4 байта
ДАТАВРЕМЯ: 8 байт
Атрибуты поля: Атрибут NULL занимает один байт.Если поле НЕ NULL, такого атрибута нет.
Возьмем два простых каштана:
mysql> EXPLAIN SELECT * FROM order_info WHERE user_id < 3 AND product_name = 'p1' AND productor = 'WHH' \G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: order_info
partitions: NULL
type: range
possible_keys: user_product_detail_index
key: user_product_detail_index
key_len: 9
ref: NULL
rows: 5
filtered: 11.11
Extra: Using where; Using index
1 row in set, 1 warning (0.00 sec)
В приведенном выше примере запрашивается указанное содержимое из таблицы order_info, и из оператора создания таблицы этой таблицы мы можем узнать, что таблицаorder_info
Существует федеративный индекс:
KEY `user_product_detail_index` (`user_id`, `product_name`, `productor`)
Однако этот запросWHERE user_id < 3 AND product_name = 'p1' AND productor = 'WHH'
, так как запрос диапазона user_id выполняется первым и в соответствии с最左前缀匹配
В принципе, когда встречается запрос диапазона, сопоставление индекса останавливается, поэтому фактически поля индекса, которые мы используем, являются толькоuser_id
, Таким образом, вEXPLAIN
, отображаемое значение key_len равно 9. Поскольку поле user_id имеет значение BIGINT, оно занимает 8 байт, а атрибут NULL занимает один байт, поэтому общее количество байтов равно 9. Если мы изменим поле user_id наBIGINT(20) NOT NULL DEFAULT '0'
, то key_length должно быть равно 8.
выше, потому что最左前缀匹配
принцип, наш запрос использует только совместный индексuser_id
поле, так что эффективность не высока.
Давайте посмотрим на следующий пример:
mysql> EXPLAIN SELECT * FROM order_info WHERE user_id = 1 AND product_name = 'p1' \G;
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: order_info
partitions: NULL
type: ref
possible_keys: user_product_detail_index
key: user_product_detail_index
key_len: 161
ref: const,const
rows: 2
filtered: 100.00
Extra: Using index
1 row in set, 1 warning (0.00 sec)
В этом запросе мы не использовали диапазонный запрос, а значение key_len равно 161. Почему? Потому что условия нашего запросаWHERE user_id = 1 AND product_name = 'p1'
, используются только первые два поля в объединенном индексе, поэтомуkeyLen(user_id) + keyLen(product_name) = 9 + 50 * 3 + 2 = 161
rows
Строки также являются важным полем.Оптимизатор запросов MySQL оценивает количество строк данных, которые SQL должен просмотреть и прочитать, чтобы найти результирующий набор на основе статистики.
Это значение очень удобно для демонстрации эффективности SQL.В принципе, чем меньше строк, тем лучше.
Extra
Много дополнительной информации в EXplain будет отображаться в поле «Дополнительно», общие из них следующие:
Using filesort
когда Экстра имеетUsing filesort
, это означает, что MySQL нуждается в дополнительных операциях сортировки, и эффект сортировки не может быть достигнут за счет порядка индекса.Using filesort
, рекомендуется его оптимизировать и удалить, так как такой запрос потребляет много ресурсов процессора.
Например следующий пример:
mysql> EXPLAIN SELECT * FROM order_info ORDER BY product_name \G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: order_info
partitions: NULL
type: index
possible_keys: NULL
key: user_product_detail_index
key_len: 253
ref: NULL
rows: 9
filtered: 100.00
Extra: Using index; Using filesort
1 row in set, 1 warning (0.00 sec)
Наш индекс
KEY `user_product_detail_index` (`user_id`, `product_name`, `productor`)
Но в приведенном выше запросе в соответствии сproduct_name
для сортировки и, следовательно, не может быть оптимизирован с помощью индекса, который, в свою очередь, производитUsing filesort
.
Если мы изменим сортировку наORDER BY user_id, product_name
, то не появитсяUsing filesort
. Например:
mysql> EXPLAIN SELECT * FROM order_info ORDER BY user_id, product_name \G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: order_info
partitions: NULL
type: index
possible_keys: NULL
key: user_product_detail_index
key_len: 253
ref: NULL
rows: 9
filtered: 100.00
Extra: Using index
1 row in set, 1 warning (0.00 sec)
Using index
«Покрывающее сканирование индекса», что означает, что запрос может найти необходимые данные в дереве индексов без сканирования файла данных таблицы, что часто означает хорошую производительность.Using temporary
Запросы используют временные таблицы, которые обычно появляются при сортировке, группировке и объединении нескольких таблиц. Эффективность запросов невысока, поэтому рекомендуется оптимизация.