Резкая разница в скорости запросов
Я не понимаю разницу (строка 2) этих двух EXPLAIN
s. Может быть, у меня есть подсказка, почему mysql так сильно отличается от тех, что сильно влияет на скорость запросов.
Медленный запрос длится 12 секунд (что соответствует запросу всех строк с этим запросом) и использует объединение целочисленных столбцов, в то время как объединенная таблица содержит только 3 записи:
SELECT `inv_assets`.`id` AS `id`, `site`.`description` AS `sitename`,
(SELECT COALESCE(DATE_FORMAT(CONVERT_TZ(MIN(inspdate),'UTC','Europe/Vienna'),'%Y-%m-%d'),'')
FROM `mobuto_inv_inspections` AS `nextinsp`
WHERE ((`nextinsp`.`objectlink` = `inv_assets`.`id`
AND `nextinsp`.`inspdate` >= NOW()))
) AS `nextinsp`
FROM `mobuto_inv_assets` AS `inv_assets`
LEFT JOIN `mobuto_inv_sites` AS `site`
ON (`site`.`siteid` = `inv_assets`.`site`
AND `site`.`_state` IN (2,0))
ORDER BY `inv_assets`.`type` ASC LIMIT 0, 20;
+----+--------------------+------------+--------+----------------+---------+---------+------------------------------+-------+----------------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+--------------------+------------+--------+----------------+---------+---------+------------------------------+-------+----------------------------------------------------+
| 1 | PRIMARY | inv_assets | ALL | NULL | NULL | NULL | NULL | 24857 | Using temporary; Using filesort |
| 1 | PRIMARY | site | ALL | PRIMARY,_state | NULL | NULL | NULL | 3 | Using where; Using join buffer (Block Nested Loop) |
| 2 | DEPENDENT SUBQUERY | nextinsp | ALL | inspdate | NULL | NULL | NULL | 915 | Using where |
+----+--------------------+------------+--------+----------------+---------+---------+------------------------------+-------+----------------------------------------------------+
Быстрый запрос занимает всего несколько долей секунды, использует столбцы объединения по varchar(32), а объединенная таблица содержит 1352 записи:
SELECT `inv_assets`.`id` AS `id`, `guarantor`.`lastname` AS `guarantoruname`,
(SELECT COALESCE(DATE_FORMAT(CONVERT_TZ(MIN(inspdate),'UTC','Europe/Vienna'),'%Y-%m-%d'),'')
FROM `mobuto_inv_inspections` AS `nextinsp`
LEFT JOIN `users` AS `saveuser`
ON (`saveuser`.`uid` = `nextinsp`.`saveuser`
AND `saveuser`.`_state` = '0')
WHERE ((`nextinsp`.`objectlink` = `inv_assets`.`id`
AND `nextinsp`.`inspdate` >= NOW()))
) AS `nextinsp`
FROM `mobuto_inv_assets` AS `inv_assets`
LEFT JOIN `users` AS `guarantor`
ON (`guarantor`.`uid` = `inv_assets`.`guarantor`
AND `guarantor`.`_state` = '0')
ORDER BY `inv_assets`.`type` ASC LIMIT 0, 20;
+----+--------------------+------------+--------+----------------+---------+---------+---------------------------------+-------+----------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+--------------------+------------+--------+----------------+---------+---------+---------------------------------+-------+----------------+
| 1 | PRIMARY | inv_assets | ALL | NULL | NULL | NULL | NULL | 24857 | Using filesort |
| 1 | PRIMARY | guarantor | eq_ref | PRIMARY,_state | PRIMARY | 98 | mobuto_dev.inv_assets.guarantor | 1 | Using where |
| 2 | DEPENDENT SUBQUERY | nextinsp | ALL | inspdate | NULL | NULL | NULL | 915 | Using where |
| 2 | DEPENDENT SUBQUERY | saveuser | eq_ref | PRIMARY,_state | PRIMARY | 98 | mobuto_dev.nextinsp.saveuser | 1 | Using where |
+----+--------------------+------------+--------+----------------+---------+---------+---------------------------------+-------+----------------+
Странная вещь для меня, когда я удаляю столбец (description
) объединенной таблицы в 'column-select-part' (хотя объединение все еще сохраняется, и IMHO mysql не оптимизирует его, когда он не используется), скорость возвращается (потому что mysql больше не использует временную таблицу и объяснение выглядит так же, как быстрый, имея type=eq_ref
).
Но почему это работает для первого образца, только когда столбец не выбран, тогда как я могу выбрать один во втором!?
CREATE TABLE `mobuto_inv_assets` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`invnum` varchar(10) NOT NULL,
`oebglcat` varchar(4) NOT NULL,
`mark` varchar(100) NOT NULL,
`type` varchar(100) NOT NULL,
`serialnum` varchar(100) NOT NULL,
`desc` varchar(100) NOT NULL,
`site` int(11) NOT NULL DEFAULT '0',
`licnum` varchar(20) NOT NULL DEFAULT '',
`inquirer` varchar(100) NOT NULL DEFAULT '',
`inqdate` date NOT NULL DEFAULT '0000-00-00',
`supplier` varchar(100) NOT NULL DEFAULT '',
`suppldate` date NOT NULL DEFAULT '0000-00-00',
`supplnumber` varchar(30) NOT NULL DEFAULT '',
`invoicedate` date NOT NULL DEFAULT '0000-00-00',
`invoicenumber` varchar(30) NOT NULL DEFAULT '',
`purchaseprice` decimal(11,2) NOT NULL DEFAULT '0.00',
`leased` varchar(1) NOT NULL DEFAULT 'N',
`leasingcompany` varchar(100) NOT NULL DEFAULT '',
`leasingnumber` varchar(30) NOT NULL DEFAULT '',
`notes` text NOT NULL,
`inspnotes` text NOT NULL,
`inactive` varchar(1) NOT NULL DEFAULT 'N',
`maintain` varchar(1) NOT NULL DEFAULT 'Y',
`asset` varchar(1) NOT NULL DEFAULT 'Y',
`inspection` varchar(1) NOT NULL DEFAULT '',
`inspperson` varchar(100) NOT NULL DEFAULT '',
`guarantor` varchar(32) NOT NULL DEFAULT '',
`saveuser` varchar(32) NOT NULL,
`savetime` int(11) NOT NULL,
`recordid` varchar(32) NOT NULL,
`_state` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
UNIQUE KEY `invnum` (`invnum`),
KEY `_state` (`_state`),
KEY `site` (`site`)
) ENGINE=InnoDB AUTO_INCREMENT=30707 DEFAULT CHARSET=utf8;
CREATE TABLE `mobuto_inv_sites` (
`siteid` int(11) NOT NULL AUTO_INCREMENT,
`description` varchar(100) NOT NULL,
`saveuser` varchar(32) NOT NULL,
`savetime` int(11) NOT NULL,
`recordid` varchar(32) NOT NULL,
`_state` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`siteid`),
KEY `_state` (`_state`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
mysql> SHOW INDEX FROM mobuto_inv_assets;
+-------------------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-------------------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| mobuto_inv_assets | 0 | PRIMARY | 1 | id | A | 24857 | NULL | NULL | | BTREE | | |
| mobuto_inv_assets | 0 | invnum | 1 | invnum | A | 24857 | NULL | NULL | | BTREE | | |
| mobuto_inv_assets | 1 | _state | 1 | _state | A | 4 | NULL | NULL | | BTREE | | |
+-------------------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
Изменения в соответствии с просьбой @Wilson Hauck:
- Добавлен индекс в столбец
site
вmobuto_inv_assets
(уменьшена скорость выполнения почти на полсекунды) - Кажется, что столбец
nextinsp
отсутствовал в первом запросе. Возможно потерян во время форматирования запроса. Конечно должно быть так же как в быстром - Удалил
saveuser
присоединиться, поскольку он там не используется (сохранил еще 2 секунды) и обновил свойEXPLAIN
(последняя строка удалена) SHOW INDEX FROM mobuto_inv_sites
добавленной+------------------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ | Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | +------------------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ | mobuto_inv_sites | 0 | PRIMARY | 1 | siteid | A | 3 | NULL | NULL | | BTREE | | | | mobuto_inv_sites | 1 | _state | 1 | _state | A | 3 | NULL | NULL | | BTREE | | | +------------------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
4 ответа
Ваш первый запрос использует меньше ключей, чем второй. possible_keys
столбец в плане объяснения показывает, где ключи доступны для использования, однако key
столбец показывает, где они на самом деле используются.
Я бы посоветовал, если вы не видите структуру вашей БД, активнее использовать эти ключи в предложениях JOIN и WHERE, чтобы ускорить ее.
Я бы удостоверился, что запрос не кэшируется, когда вы говорите, что изменяете столбцы выбора, а скорость меняется.
Спасибо за размещение ваших двух SHOW CREATE TABLE, очень полезных. Пожалуйста, рассмотрите возможность добавления индекса с помощью сайта ALTER TABLE mobuto_inv_sites ADD INDEX - если в вашей системе достаточно места. Кроме того, показ EXPLAIN для query1 не соответствует запросу. Запрос не ссылается на nextinsp или saveused, которые я вижу в EXPLAIN. Пожалуйста, замените EXPLAIN для query1 после создания индекса, когда у вас будет возможность снова протестировать и указать любое необходимое сокращение времени выполнения. Также было бы неплохо, если бы вы могли опубликовать результаты SHOW INDEX FROM mobuto_inv_sites, чтобы мы могли видеть объем ваших данных и количество элементов.
12 секунд первый запрос, вероятно, вызван подсказками столбцов ROWS из 24857*3*915*1 = 68 232 465 рассмотренных строк. Менее 1 секунды для подсказок столбца ROWS второго запроса из 24857*1*915*1 = 22 744 155 рассмотренных строк. Использование первого запроса обработки блочных вложенных циклов является основной причиной задержки ответа.
Пожалуйста, опубликуйте результаты SHOW CREATE TABLE mobuto_inv_assets и mobuto_inv_sites. Пожалуйста, также опубликуйте результаты SHOW INDEX FROM mobuto_inv_assets и mobuto_inv_sites. С помощью этой дополнительной информации кто-то может предложить усовершенствования запросов SELECT ...., которые позволят избежать обработки блочно-вложенных циклов, которая сильно загружает ЦП с помощью RBAR (обработка строк за агонизирующей строкой). Может потребоваться дополнительная индексация.
Если строки inv_assets заполнены данными ACCURATE _state, рассмотрите возможность изменения query1 на что-то вроде следующего:
ВЫБРАТЬ inv_assets
,id
КАК id
, site
,description
КАК sitename
, (ВЫБЕРИТЕ COALESCE(DATE_FORMAT(CONVERT_TZ(MIN(вдох)),'UTC','Европа / Вена'),'%Y-%m-%d'),'' ОТ mobuto_inv_inspections
КАК nextinsp
ГДЕ ((nextinsp
,objectlink
знак равно inv_assets
,id
А ТАКЖЕ nextinsp
,inspdate
> = СЕЙЧАС ()))) КАК nextinsp
ОТ mobuto_inv_assets
КАК inv_assets
ГДЕ inv_assets
,_state
= 2 ИЛИ inv_assets
,_state
= 0
ЛЕВЫЙ РЕЙТИНГ mobuto_inv_sites
КАК site
ON (site
,siteid
знак равно inv_assets
,site
А ТАКЖЕ site
,_state
IN (2,0) ЗАКАЗАТЬ inv_assets
,type
ASC LIMIT 0, 20;
EXPLAIN должен избегать сканирования таблицы и последующей обработки блочных вложенных циклов.
Если _state data в inv_assets не является ТОЧНЫМ для каждой строки, это не будет работать.
2017-08-10 обновление 09:42 CT, пожалуйста, опубликуйте запрос QUERY, EXPLAIN, SHOW CREATE TABLE tblname для задействованных таблиц и SHOW INDEX FROM tblname для задействованных таблиц.