На веб-сайте, я использую Джанго, чтобы сделать некоторые запросы:медленно MySQL "INNER JOIN"
Джанго линия:
CINodeInventory.objects.select_related().filter(ci_class__type='equipment',company__slug=self.kwargs['company'])
генерирует запрос MySQL так:
SELECT *
FROM `inventory_cinodeinventory`
INNER JOIN `ci_cinodeclass` ON (`inventory_cinodeinventory`.`ci_class_id` = `ci_cinodeclass`.`class_name`)
INNER JOIN `accounts_companyprofile` ON (`inventory_cinodeinventory`.`company_id` = `accounts_companyprofile`.`slug`)
INNER JOIN `accounts_companysite` ON (`inventory_cinodeinventory`.`company_site_id` = `accounts_companysite`.`slug`)
INNER JOIN `accounts_companyprofile` T5 ON (`accounts_companysite`.`company_id` = T5.`slug`)
WHERE (
`ci_cinodeclass`.`type` = 'equipment'
AND `inventory_cinodeinventory`.`company_id` = 'thecompany'
)
ORDER BY `inventory_cinodeinventory`.`name` ASC
Проблема в том, что для всего 40 000 записей в основной таблице требуется 0,5 секунды для обработки.
Я проверил все индексы, создаю те, которые необходимы для сортировки или соединения: у меня все еще есть проблема.
Самое смешное, что если я заменил последний INNER JOIN LEFT JOIN, запрос будет в 10 раз быстрее! К сожалению, поскольку я использую django для запроса, у меня нет доступа к запросам SQL, которые он генерирует (я не хочу сам делать сам SQL).
для последнего присоединиться как "INNER JOIN" Explain, дает:
+----+-------------+---------------------------+--------+----------------------------------------------------------------------------------------------------------+------------------------------------+---------+------------------------------------------------+-------+---------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------------------------+--------+----------------------------------------------------------------------------------------------------------+------------------------------------+---------+------------------------------------------------+-------+---------------------------------+
| 1 | SIMPLE | accounts_companyprofile | const | PRIMARY | PRIMARY | 152 | const | 1 | Using temporary; Using filesort |
| 1 | SIMPLE | inventory_cinodeinventory | range | inventory_cinodeinventory_41ddcf59,inventory_cinodeinventory_543518c6,inventory_cinodeinventory_14fe63e9 | inventory_cinodeinventory_543518c6 | 152 | NULL | 42129 | Using where |
| 1 | SIMPLE | T5 | ALL | PRIMARY | NULL | NULL | NULL | 3 | Using join buffer |
| 1 | SIMPLE | accounts_companysite | eq_ref | PRIMARY,accounts_companysite_543518c6 | PRIMARY | 152 | cidb.inventory_cinodeinventory.company_site_id | 1 | Using where |
| 1 | SIMPLE | ci_cinodeclass | eq_ref | PRIMARY | PRIMARY | 92 | cidb.inventory_cinodeinventory.ci_class_id | 1 | Using where |
+----+-------------+---------------------------+--------+----------------------------------------------------------------------------------------------------------+------------------------------------+---------+------------------------------------------------+-------+---------------------------------+
Для последнего присоединиться в качестве "LEFT JOIN", я получил:
+----+-------------+---------------------------+--------+----------------------------------------------------------------------------------------------------------+---------+---------+------------------------------------------------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------------------------+--------+----------------------------------------------------------------------------------------------------------+---------+---------+------------------------------------------------+------+-------------+
| 1 | SIMPLE | accounts_companyprofile | const | PRIMARY | PRIMARY | 152 | const | 1 | |
| 1 | SIMPLE | inventory_cinodeinventory | index | inventory_cinodeinventory_41ddcf59,inventory_cinodeinventory_543518c6,inventory_cinodeinventory_14fe63e9 | name | 194 | NULL | 173 | Using where |
| 1 | SIMPLE | accounts_companysite | eq_ref | PRIMARY | PRIMARY | 152 | cidb.inventory_cinodeinventory.company_site_id | 1 | |
| 1 | SIMPLE | T5 | eq_ref | PRIMARY | PRIMARY | 152 | cidb.accounts_companysite.company_id | 1 | |
| 1 | SIMPLE | ci_cinodeclass | eq_ref | PRIMARY | PRIMARY | 92 | cidb.inventory_cinodeinventory.ci_class_id | 1 | Using where |
+----+-------------+---------------------------+--------+----------------------------------------------------------------------------------------------------------+---------+---------+------------------------------------------------+------+-------------+
, кажется, для " INNER JOIN ", MySQL не находит индексов для соединения T5: почему?
Профилирование дает:
starting 0.000011
checking query cache for query 0.000086
Opening tables 0.000014
System lock 0.000005
Table lock 0.000052
init 0.000064
optimizing 0.000021
statistics 0.000180
preparing 0.000024
Creating tmp table 0.000308
executing 0.000003
Copying to tmp table 0.353414 !!!
Sorting result 0.037244
Sending data 0.035168
end 0.000005
removing tmp table 0.550974 !!!
end 0.000009
query end 0.000003
freeing items 0.000113
storing result in query cache 0.000009
logging slow query 0.000002
cleaning up 0.000004
Таким образом, кажется, есть шаг, где MySQL использует временную таблицу. Этот шаг не происходит с LEFT JOIN, только с INNER JOIN. Я пытался избежать этого, включив в «индекс силы для присоединиться к» в запросе, но это не помогло ...
присоединиться таблицы:
CREATE TABLE IF NOT EXISTS `accounts_companysite` (
`slug` varchar(50) NOT NULL,
`created` datetime NOT NULL,
`modified` datetime NOT NULL,
`deleted` tinyint(1) NOT NULL,
`company_id` varchar(50) NOT NULL,
`name` varchar(128) NOT NULL,
`address` longtext NOT NULL,
`city` varchar(64) NOT NULL,
`zip_code` varchar(6) NOT NULL,
`state` varchar(32) NOT NULL,
`country` varchar(2) DEFAULT NULL,
`phone` varchar(20) NOT NULL,
`fax` varchar(20) NOT NULL,
`more` longtext NOT NULL,
PRIMARY KEY (`slug`),
KEY `accounts_companysite_543518c6` (`company_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `accounts_companyprofile` (
`slug` varchar(50) NOT NULL,
`created` datetime NOT NULL,
`modified` datetime NOT NULL,
`deleted` tinyint(1) NOT NULL,
`name` varchar(128) NOT NULL,
`address` longtext NOT NULL,
`city` varchar(64) NOT NULL,
`zip_code` varchar(6) NOT NULL,
`state` varchar(32) NOT NULL,
`country` varchar(2) DEFAULT NULL,
`phone` varchar(20) NOT NULL,
`fax` varchar(20) NOT NULL,
`contract_id` varchar(32) NOT NULL,
`more` longtext NOT NULL,
PRIMARY KEY (`slug`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `inventory_cinodeinventory` (
`uuid` varchar(36) NOT NULL,
`name` varchar(64) NOT NULL,
`synopsis` varchar(64) NOT NULL,
`path` varchar(255) NOT NULL,
`created` datetime NOT NULL,
`modified` datetime NOT NULL,
`deleted` tinyint(1) NOT NULL,
`root_id` varchar(36) DEFAULT NULL,
`parent_id` varchar(36) DEFAULT NULL,
`order` int(11) NOT NULL,
`ci_class_id` varchar(30) NOT NULL,
`data` longtext NOT NULL,
`serial` varchar(64) NOT NULL,
`company_id` varchar(50) NOT NULL,
`company_site_id` varchar(50) NOT NULL,
`vendor` varchar(48) NOT NULL,
`type` varchar(64) NOT NULL,
`model` varchar(64) NOT NULL,
`room` varchar(30) NOT NULL,
`rack` varchar(30) NOT NULL,
`rack_slot` varchar(30) NOT NULL,
PRIMARY KEY (`uuid`),
KEY `inventory_cinodeinventory_1fb5ff88` (`root_id`),
KEY `inventory_cinodeinventory_63f17a16` (`parent_id`),
KEY `inventory_cinodeinventory_41ddcf59` (`ci_class_id`),
KEY `inventory_cinodeinventory_543518c6` (`company_id`),
KEY `inventory_cinodeinventory_14fe63e9` (`company_site_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
Я также попытался мелодии MySQL, добавив в моем .cnf:
join_buffer_size = 16M
tmp_table_size = 160M
max_seeks_for_key = 100
... но это не поможет.
С django легко использовать Postgresql вместо Mysql, поэтому я попробовал: с тем же запросом и с теми же данными в db, postgres намного быстрее, чем Mysql: x10 быстрее при использовании INNER JOIN (анализ показывает это использует индексы в отличие от Mysql)
У вас есть идея, почему мой MySQL INNER JOIN настолько медленный?
EDIT 1:
после некоторого тестирования, я уменьшить проблему на этот запрос:
SELECT *
FROM `inventory_cinodeinventory`
INNER JOIN `accounts_companyprofile` ON `inventory_cinodeinventory`.`company_id` = `accounts_companyprofile`.`slug`
ORDER BY `inventory_cinodeinventory`.`name` ASC
Этот запрос очень медленно, и я не понимаю, почему. Без «ORDER BY» п, это быстро, но не с ним, хотя индекс имя устанавливается:
CREATE TABLE IF NOT EXISTS `inventory_cinodeinventory` (
`uuid` varchar(36) NOT NULL,
`name` varchar(64) NOT NULL,
`synopsis` varchar(64) NOT NULL,
`path` varchar(255) NOT NULL,
`created` datetime NOT NULL,
`modified` datetime NOT NULL,
`deleted` tinyint(1) NOT NULL,
`root_id` varchar(36) DEFAULT NULL,
`parent_id` varchar(36) DEFAULT NULL,
`order` int(11) NOT NULL,
`ci_class_id` varchar(30) NOT NULL,
`data` longtext NOT NULL,
`serial` varchar(64) NOT NULL,
`company_id` varchar(50) NOT NULL,
`company_site_id` varchar(50) NOT NULL,
`vendor` varchar(48) NOT NULL,
`type` varchar(64) NOT NULL,
`model` varchar(64) NOT NULL,
`room` varchar(30) NOT NULL,
`rack` varchar(30) NOT NULL,
`rack_slot` varchar(30) NOT NULL,
PRIMARY KEY (`uuid`),
KEY `inventory_cinodeinventory_1fb5ff88` (`root_id`),
KEY `inventory_cinodeinventory_63f17a16` (`parent_id`),
KEY `inventory_cinodeinventory_41ddcf59` (`ci_class_id`),
KEY `inventory_cinodeinventory_14fe63e9` (`company_site_id`),
KEY `inventory_cinodeinventory_543518c6` (`company_id`,`name`),
KEY `name` (`name`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
EDIT 2:
Предыдущий запрос может быть решена с помощью «СИЛЫ УКАЗАТЕЛЬ ДЛЯ ЗАКАЗА BY (имя). К сожалению, этот совет не работает с первым запросом в моей теме ...
EDIT 3:
я восстановил базу данных с заменой «UUID» первичные ключами от VARCHAR до целого: это не помогает все ... плохие новости.
EDIT 4:
Я попытался Mysql 5.5.20: не лучше. Postgresql 8.4 в 10 раз быстрее для этого конкретного запроса.
Я изменил немного о resquest (удален Т5 присоединиться):
SELECT *
FROM `inventory_cinodeinventory`
INNER JOIN `ci_cinodeclass` ON (`inventory_cinodeinventory`.`ci_class_id` = `ci_cinodeclass`.`class_name`)
INNER JOIN `accounts_companyprofile` ON (`inventory_cinodeinventory`.`company_id` = `accounts_companyprofile`.`slug`)
INNER JOIN `accounts_companysite` ON (`inventory_cinodeinventory`.`company_site_id` = `accounts_companysite`.`slug`)
WHERE (
`ci_cinodeclass`.`type` = 'equipment'
AND `inventory_cinodeinventory`.`company_id` = 'thecompany'
)
ORDER BY `inventory_cinodeinventory`.`name` ASC
Это работает хорошо, но у меня есть некоторые другие запросы, просто немного разные, где этот трюк не работает.
На самом деле, после поиска, кажется, что как только вы присоединитесь к двум таблицам, которые имеют «много общего», то есть половина строк правой таблицы может быть объединена с теми, которые находятся в левой таблице (это мой случай): Mysql предпочитает использовать таблицы сканирование вместо индекса: быстрее, я нашел где-то (!!)
установить и настроить панель инструментов django-debug, и вы сможете легко увидеть, как sql django генерирует http://pypi.python.org/pypi/django-debug-toolbar , Вам определенно нужен select_related для предполагаемого использования? – shawnwall
Да, мне абсолютно нужен select_related, и Да, я уже использую панель инструментов django-debug: запрос SQL, который я дал, поступает из этого инструмента. (для удобства чтения в запросе я просто помещаю «*» вместо длинного списка запрошенных столбцов) – Eric
В вашем объяснении есть индекс «inventory_cinodeinventory.name», но его нет в вашей схеме. Что-то не в порядке. –