У меня возникают проблемы с определенной производительностью запросов, у меня есть следующие 2 таблицы:
CREATE TABLE `customers` (
`CustFullName` varchar(45) NOT NULL,
`CustPassword` varchar(45) NOT NULL,
`CustEmail` varchar(128) NOT NULL,
`SocialNetworkId` tinyint(4) NOT NULL,
`CustUID` varchar(64) CHARACTER SET ascii NOT NULL,
`CustMoney` bigint(20) NOT NULL DEFAULT '0',
`LastIpAddress` varchar(45) CHARACTER SET ascii NOT NULL,
`LastLoginTime` datetime NOT NULL DEFAULT '1900-10-10 10:10:10',
`SmallPicURL` varchar(120) CHARACTER SET ascii DEFAULT '',
`LargePicURL` varchar(120) CHARACTER SET ascii DEFAULT '',
`LuckyChips` int(10) unsigned NOT NULL DEFAULT '0',
`AccountCreationTime` datetime NOT NULL DEFAULT '2009-11-11 11:11:11',
`AccountStatus` tinyint(4) NOT NULL DEFAULT '1',
`CustLevel` int(11) NOT NULL DEFAULT '0',
`City` varchar(32) NOT NULL DEFAULT '',
`State` varchar(32) NOT NULL DEFAULT '0',
`Country` varchar(32) NOT NULL DEFAULT '',
`Zip` varchar(16) CHARACTER SET ascii NOT NULL,
`CustExp` bigint(20) NOT NULL DEFAULT '0',
PRIMARY KEY (`CustUID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
И:
CREATE TABLE `mutualfriends` (
`CustUID` varchar(32) CHARACTER SET ascii NOT NULL,
`CustUID2` varchar(32) CHARACTER SET ascii NOT NULL,
`FType` tinyint(4) NOT NULL,
PRIMARY KEY (`CustUID`,`CustUID2`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8
таблица клиентов содержит 1M строк и mutalfriends размером около 50 тыс. строк.
Мне нужны результаты следующего запроса:
SELECT c.CustUID, c.CustFullName, c.CustMoney, c.SmallPicURL
FROM `customers` c
WHERE c.`CustUID` = '9:2'
OR c.`CustUID` IN
(SELECT m.CustUID2 FROM mutualfriends m WHERE m.CustUID = '9:2');
OR c.`CustUID` IN
(SELECT m.CustUID FROM mutualfriends m WHERE m.CustUID2 = '9:2');
по какой-то причине я не понимаю, этот запрос занимает около 10 секунд, чтобы закончить. Подзапросы содержат не более трех строк каждый, если я помещаю константы вместо:
(SELECT m.CustUID2 FROM mutualfriends m WHERE m.CustUID = '9:2');
И:
(SELECT m.CustUID FROM mutualfriends m WHERE m.CustUID2 = '9:2');
Например:
SELECT c.CustUID, c.CustFullName, c.CustMoney, c.SmallPicURL
FROM `customers` c
WHERE c.`CustUID` = '9:2'
OR c.`CustUID` IN
('9:3','9:4','9:5');
OR c.`CustUID` IN
('9:6','9:7');
Затем для завершения запроса требуется несколько мс. Что я делаю неправильно здесь, этот запрос не должен занимать больше нескольких мс...
Также я могу добавить, что эта часть запроса:
(SELECT m.CustUID2 FROM mutualfriends m WHERE m.CustUID = '9:2');
(SELECT m.CustUID FROM mutualfriends m WHERE m.CustUID2 = '9:2');
также принимает несколько мс....
Некоторые случайные догадки:
(SELECT m.CustUID FROM mutualfriends m WHERE m.CustUID2 = '9:2');
... возможно не может эффективно использовать индекс (CustUID, CustUID2). Вы должны проверить план объяснения. Если это не так, подумайте о добавлении еще одного uniue-индекса (CustUID2, CustUID).
Выражения вроде
WHERE <constant>
OR <subquery>
... генерирует дрянной план в Oracle. Может быть, это так и для MySQL? Его обычно можно решить, если вы как-то отредактируете константное выражение внутри подзапроса или немного перефразируете логику.
Еще одна идея - выполнить три запроса и СОЕДИНИТЬ их вместе. Если вы ожидаете возвращения многих строк, этот параметр менее привлекателен, поскольку сортировка приведет к снижению производительности.
select *
from customer
where CustUID = '9:2'
union
select *
from customer
where CustUID in(select m.CustUID from mutualfriends m where m.CustUID2 = '9:2')
union
select *
from customer
where CustUID in(select m.CustUID2 from mutualfriends m where m.CustUID = '9:2')
Посмотрите http://dev.mysql.com/doc/refman/5.0/en/using-explain.html. Также быстрый поиск Google вернется с множеством отправных точек для обработки оптимизаций SQL. Большинство полагаются только на EXPLAIN
, поэтому начните там.
Что касается вашего запроса, CustUID2
вызовет полное сканирование таблицы, поскольку оно не будет индексироваться напрямую. Кроме того, будьте осторожны с использованием подзапросов, так как я встречал некоторые случаи, когда оптимизатор не мог полностью оптимизировать подзапрос... в моем случае этого было достаточно, чтобы разделить запрос на большее количество запросов (без подзапросов),
Это также может помочь использовать JOIN
, поскольку они обрабатываются иначе, чем подзапросы.
В сети есть несколько страниц, которые расскажут вам, как оптимизировать запросы, но я предполагаю, что MySQL не использует индекс для взаимных друзей, потому что он состоит из двух столбцов.