2015-08-26 2 views
1

У меня есть 2 страницы поиска, которые очень медленны в получении результатов.Как улучшить запрос MYSQL с несколькими JOINS

Я не писал запросы, но я знаю, что они не написаны эффективным образом; Мне просто не хватает практики в MYSQL, чтобы выяснить, как сделать их более эффективными.

Что мне делать, чтобы улучшить следующий запрос?

SELECT DISTINCT m.id AS memberID , m.login , m.age , p.gender 
, p.name AS header , p.id AS profileID , p.city , p.state , p.lastlogin 
, o.login AS online , c.name AS country , ph.filename_1 AS pic 
FROM dt_members AS m 
INNER JOIN dt_profile_approved AS p ON m.id=p.member_id 
LEFT JOIN dt_privacy AS pv ON m.id=pv.member_id 
INNER JOIN dt_countries AS c ON c.id=p.country 
LEFT JOIN dt_photos AS ph ON m.id=ph.member_id 
LEFT JOIN dt_usersonline AS o ON m.login=o.login 
WHERE p.status=1 AND (pv.unsearchable IS NULL OR pv.unsearchable='') 
AND p.gender='Female' AND m.age BETWEEN 25 AND 40 
ORDER BY p.lastlogin DESC 
LIMIT 0, 21; 

Это ужасно медленно и часто показывает 500 ошибок.

Выход объяснить:

EXPLAIN RESULTS

Показать Создать таблицу:

CREATE TABLE `dt_members` (
`id` int(11) NOT NULL AUTO_INCREMENT, 
`login` varchar(25) DEFAULT NULL, 
`pswd` varchar(20) DEFAULT NULL, 
`email` varchar(255) DEFAULT NULL, 
`name` varchar(40) DEFAULT NULL, 
`gender` varchar(10) DEFAULT NULL, 
`age` int(11) DEFAULT NULL, 
`country` varchar(255) DEFAULT NULL, 
`looking_for` varchar(255) DEFAULT NULL, 
`ip_addr` varchar(15) DEFAULT NULL, 
`reg_date` int(11) DEFAULT NULL, 
`status` int(11) DEFAULT NULL, 
`system_status` int(11) DEFAULT '0', 
`system_status_end` int(11) DEFAULT NULL, 
`unlimited` int(11) DEFAULT '0', 
`unlimited_end` int(11) DEFAULT NULL, 
`matchfinder` int(1) DEFAULT '0', 
PRIMARY KEY (`id`), 
KEY `login` (`login`), 
KEY `pswd` (`pswd`), 
KEY `email` (`email`), 
KEY `name` (`name`), 
KEY `gender` (`gender`), 
KEY `age` (`age`), 
KEY `country` (`country`), 
KEY `looking_for` (`looking_for`), 
KEY `ip_addr` (`ip_addr`), 
KEY `reg_date` (`reg_date`), 
KEY `status` (`status`), 
KEY `system_status` (`system_status`), 
KEY `system_status_end` (`system_status_end`), 
KEY `unlimited` (`unlimited`), 
KEY `unlimited_end` (`unlimited_end`), 
KEY `matchfinder` (`matchfinder`) 
) ENGINE=MyISAM AUTO_INCREMENT=29150 DEFAULT CHARSET=latin1 

CREATE TABLE `dt_profile` (
`id` int(11) NOT NULL AUTO_INCREMENT, 
`member_id` int(11) DEFAULT NULL, 
`country` int(11) DEFAULT NULL, 
`state` varchar(255) DEFAULT NULL, 
`city` varchar(255) DEFAULT NULL, 
`email` varchar(255) DEFAULT NULL, 
`name` varchar(255) DEFAULT NULL, 
`gender` varchar(20) DEFAULT NULL, 
`birth_day` int(11) DEFAULT NULL, 
`birth_month` varchar(6) DEFAULT NULL, 
`birth_year` int(11) DEFAULT NULL, 
`marital_status` int(11) DEFAULT NULL, 
`children` int(11) DEFAULT NULL, 
`drinking` int(11) DEFAULT NULL, 
`smoking` int(11) DEFAULT NULL, 
`food` int(11) DEFAULT NULL, 
`eye_color` int(11) DEFAULT NULL, 
`hair_color` int(11) DEFAULT NULL, 
`height` int(11) DEFAULT NULL, 
`body_type` int(11) DEFAULT NULL, 
`race` int(11) DEFAULT NULL, 
`religion` int(11) DEFAULT NULL, 
`occupation` int(11) DEFAULT NULL, 
`education` int(11) DEFAULT NULL, 
`lang_1` int(11) DEFAULT NULL, 
`lang_1_rate` int(11) DEFAULT NULL, 
`lang_2` int(11) DEFAULT NULL, 
`lang_2_rate` int(11) DEFAULT NULL, 
`lang_3` int(11) DEFAULT NULL, 
`lang_3_rate` int(11) DEFAULT NULL, 
`lang_4` int(11) DEFAULT NULL, 
`lang_4_rate` int(11) DEFAULT NULL, 
`looking_for` varchar(10) DEFAULT NULL, 
`age_from` int(11) DEFAULT NULL, 
`age_to` int(11) DEFAULT NULL, 
`general_info` text, 
`appearance_info` text, 
`looking_for_info` text, 
`status` int(11) DEFAULT NULL, 
`finish_status` int(11) DEFAULT NULL, 
`not_newbie` int(11) DEFAULT NULL, 
`lastlogin` int(10) NOT NULL DEFAULT '0', 
`zipcode` varchar(5) NOT NULL DEFAULT '', 
`longitude` double DEFAULT NULL, 
`latitude` double DEFAULT NULL, 
`photo_pass` varchar(25) NOT NULL DEFAULT '', 
`view_count` int(11) DEFAULT '0', 
`wants_kids` int(11) DEFAULT NULL, 
`kids_okay` int(11) DEFAULT NULL, 
`relocate_domestic` int(11) DEFAULT NULL, 
`relocate_international` int(11) DEFAULT NULL, 
`pioneer` int(11) DEFAULT NULL, 
PRIMARY KEY (`id`), 
KEY `member_id` (`member_id`), 
KEY `country` (`country`), 
KEY `state` (`state`), 
KEY `city` (`city`), 
KEY `email` (`email`), 
KEY `name` (`name`), 
KEY `gender` (`gender`), 
KEY `birth_day` (`birth_day`), 
KEY `birth_month` (`birth_month`), 
KEY `birth_year` (`birth_year`), 
KEY `marital_status` (`marital_status`), 
KEY `children` (`children`), 
KEY `drinking` (`drinking`), 
KEY `smoking` (`smoking`), 
KEY `food` (`food`), 
KEY `eye_color` (`eye_color`), 
KEY `hair_color` (`hair_color`), 
KEY `height` (`height`), 
KEY `body_type` (`body_type`), 
KEY `race` (`race`), 
KEY `religion` (`religion`), 
KEY `occupation` (`occupation`), 
KEY `education` (`education`), 
KEY `lang_1` (`lang_1`), 
KEY `lang_1_rate` (`lang_1_rate`), 
KEY `lang_2` (`lang_2`), 
KEY `lang_2_rate` (`lang_2_rate`), 
KEY `lang_3` (`lang_3`), 
KEY `lang_3_rate` (`lang_3_rate`), 
KEY `lang_4` (`lang_4`), 
KEY `lang_4_rate` (`lang_4_rate`), 
KEY `looking_for` (`looking_for`), 
KEY `age_from` (`age_from`), 
KEY `age_to` (`age_to`), 
KEY `status` (`status`), 
KEY `finish_status` (`finish_status`), 
KEY `not_newbie` (`not_newbie`), 
KEY `lastlogin` (`lastlogin`), 
KEY `zipcode` (`zipcode`), 
KEY `longitude` (`longitude`), 
KEY `latitude` (`latitude`), 
KEY `photo_pass` (`photo_pass`), 
KEY `view_count` (`view_count`), 
KEY `wants_kids` (`wants_kids`), 
KEY `kids_okay` (`kids_okay`), 
KEY `relocate_domestic` (`relocate_domestic`), 
KEY `relocate_international` (`relocate_international`), 
KEY `pioneer` (`pioneer`) 
) ENGINE=MyISAM AUTO_INCREMENT=18389 DEFAULT CHARSET=latin1 

CREATE TABLE `dt_privacy` (
`id` int(11) NOT NULL AUTO_INCREMENT, 
`member_id` int(11) DEFAULT NULL, 
`online_yn` char(1) DEFAULT NULL, 
`vkiss_yn` char(1) DEFAULT NULL, 
`profiles_yn` char(1) DEFAULT NULL, 
`IM_yn` char(1) DEFAULT NULL, 
`featured_yn` char(1) DEFAULT NULL, 
`HL_messaged_yn` char(1) DEFAULT NULL, 
`HL_im_yn` char(1) DEFAULT NULL, 
`HL_viewed_yn` char(1) DEFAULT NULL, 
`HL_kissed_yn` char(1) DEFAULT NULL, 
`HL_favorite_yn` char(1) DEFAULT NULL, 
`unsearchable` char(1) DEFAULT NULL, 
PRIMARY KEY (`id`), 
KEY `member_id` (`member_id`), 
KEY `online_yn` (`online_yn`), 
KEY `vkiss_yn` (`vkiss_yn`), 
KEY `profiles_yn` (`profiles_yn`), 
KEY `IM_yn` (`IM_yn`), 
KEY `featured_yn` (`featured_yn`), 
KEY `HL_messaged_yn` (`HL_messaged_yn`), 
KEY `HL_im_yn` (`HL_im_yn`), 
KEY `HL_viewed_yn` (`HL_viewed_yn`), 
KEY `HL_kissed_yn` (`HL_kissed_yn`), 
KEY `HL_favorite_yn` (`HL_favorite_yn`), 
KEY `unsearchable` (`unsearchable`) 
) ENGINE=MyISAM AUTO_INCREMENT=26305 DEFAULT CHARSET=latin1 

CREATE TABLE `dt_countries` (
`id` int(11) NOT NULL AUTO_INCREMENT, 
`name` varchar(100) DEFAULT NULL, 
PRIMARY KEY (`id`), 
KEY `name` (`name`) 
) ENGINE=MyISAM AUTO_INCREMENT=226 DEFAULT CHARSET=latin1 

CREATE TABLE `dt_photos` (
`id` int(11) NOT NULL AUTO_INCREMENT, 
`member_id` varchar(255) DEFAULT NULL, 
`filename_1` varchar(255) DEFAULT NULL, 
`filename_2` varchar(255) DEFAULT NULL, 
`filename_3` varchar(255) NOT NULL DEFAULT '', 
`filename_4` varchar(255) NOT NULL DEFAULT '', 
`filename_5` varchar(255) NOT NULL DEFAULT '', 
`filename_6` varchar(255) NOT NULL DEFAULT '', 
`filename_7` varchar(255) NOT NULL DEFAULT '', 
`filename_8` varchar(255) NOT NULL DEFAULT '', 
`filename_9` varchar(255) NOT NULL DEFAULT '', 
`filename_10` varchar(255) NOT NULL DEFAULT '', 
`filename_11` varchar(255) NOT NULL DEFAULT '', 
`filename_12` varchar(255) NOT NULL DEFAULT '', 
`filename_13` varchar(255) NOT NULL DEFAULT '', 
`filename_14` varchar(255) NOT NULL DEFAULT '', 
`filename_15` varchar(255) NOT NULL DEFAULT '', 
`filename_16` varchar(255) NOT NULL DEFAULT '', 
`filename_17` varchar(255) NOT NULL DEFAULT '', 
`filename_18` varchar(255) NOT NULL DEFAULT '', 
`filename_19` varchar(255) NOT NULL DEFAULT '', 
`filename_20` varchar(255) NOT NULL DEFAULT '', 
`private_1` tinyint(1) NOT NULL DEFAULT '0', 
`private_2` tinyint(1) NOT NULL DEFAULT '0', 
`private_3` tinyint(1) NOT NULL DEFAULT '0', 
`private_4` tinyint(1) NOT NULL DEFAULT '0', 
`private_5` tinyint(1) NOT NULL DEFAULT '0', 
`private_6` tinyint(1) NOT NULL DEFAULT '0', 
`private_7` tinyint(1) NOT NULL DEFAULT '0', 
`private_8` tinyint(1) NOT NULL DEFAULT '0', 
`private_9` tinyint(1) NOT NULL DEFAULT '0', 
`private_10` tinyint(1) NOT NULL DEFAULT '0', 
`private_11` tinyint(1) NOT NULL DEFAULT '0', 
`private_12` tinyint(1) NOT NULL DEFAULT '0', 
`private_13` tinyint(1) NOT NULL DEFAULT '0', 
`private_14` tinyint(1) NOT NULL DEFAULT '0', 
`private_15` tinyint(1) NOT NULL DEFAULT '0', 
`private_16` tinyint(1) NOT NULL DEFAULT '0', 
`private_17` tinyint(1) NOT NULL DEFAULT '0', 
`private_18` tinyint(1) NOT NULL DEFAULT '0', 
`private_19` tinyint(1) NOT NULL DEFAULT '0', 
`private_20` tinyint(1) NOT NULL DEFAULT '0', 
`password` varchar(255) NOT NULL DEFAULT '', 
`description_1` varchar(255) DEFAULT NULL, 
`description_2` varchar(255) DEFAULT NULL, 
`description_3` varchar(255) NOT NULL DEFAULT '', 
`description_4` varchar(255) NOT NULL DEFAULT '', 
`description_5` varchar(255) NOT NULL DEFAULT '', 
`description_6` varchar(255) NOT NULL DEFAULT '', 
`description_7` varchar(255) NOT NULL DEFAULT '', 
`description_8` varchar(255) NOT NULL DEFAULT '', 
`description_9` varchar(255) NOT NULL DEFAULT '', 
`description_10` varchar(255) NOT NULL DEFAULT '', 
`description_11` varchar(255) NOT NULL DEFAULT '', 
`description_12` varchar(255) NOT NULL DEFAULT '', 
`description_13` varchar(255) NOT NULL DEFAULT '', 
`description_14` varchar(255) NOT NULL DEFAULT '', 
`description_15` varchar(255) NOT NULL DEFAULT '', 
`description_16` varchar(255) NOT NULL DEFAULT '', 
`description_17` varchar(255) NOT NULL DEFAULT '', 
`description_18` varchar(255) NOT NULL DEFAULT '', 
`description_19` varchar(255) NOT NULL DEFAULT '', 
`description_20` varchar(255) NOT NULL DEFAULT '', 
PRIMARY KEY (`id`), 
KEY `filename_12` (`filename_12`), 
KEY `filename_13` (`filename_13`), 
KEY `filename_14` (`filename_14`), 
KEY `filename_15` (`filename_15`), 
KEY `filename_16` (`filename_16`), 
KEY `filename_17` (`filename_17`), 
KEY `filename_18` (`filename_18`), 
KEY `filename_19` (`filename_19`), 
KEY `filename_20` (`filename_20`), 
KEY `member_id` (`member_id`), 
KEY `filename_1` (`filename_1`), 
KEY `filename_2` (`filename_2`), 
KEY `filename_3` (`filename_3`), 
KEY `filename_4` (`filename_4`), 
KEY `filename_5` (`filename_5`), 
KEY `filename_6` (`filename_6`), 
KEY `filename_7` (`filename_7`), 
KEY `filename_8` (`filename_8`), 
KEY `filename_9` (`filename_9`), 
KEY `filename_10` (`filename_10`), 
KEY `private_1` (`private_1`), 
KEY `private_2` (`private_2`), 
KEY `private_3` (`private_3`), 
KEY `private_4` (`private_4`), 
KEY `private_5` (`private_5`), 
KEY `private_6` (`private_6`), 
KEY `private_7` (`private_7`), 
KEY `private_8` (`private_8`), 
KEY `private_9` (`private_9`), 
KEY `private_10` (`private_10`), 
KEY `private_11` (`private_11`), 
KEY `private_12` (`private_12`), 
KEY `private_13` (`private_13`), 
KEY `private_14` (`private_14`), 
KEY `private_15` (`private_15`), 
KEY `private_16` (`private_16`), 
KEY `private_17` (`private_17`), 
KEY `private_18` (`private_18`), 
KEY `private_19` (`private_19`), 
KEY `private_20` (`private_20`), 
KEY `password` (`password`), 
KEY `description_1` (`description_1`), 
KEY `description_2` (`description_2`), 
KEY `description_3` (`description_3`), 
KEY `description_4` (`description_4`), 
KEY `description_5` (`description_5`), 
KEY `description_6` (`description_6`), 
KEY `description_7` (`description_7`), 
KEY `description_8` (`description_8`), 
KEY `description_9` (`description_9`), 
KEY `description_10` (`description_10`), 
KEY `description_11` (`description_11`), 
KEY `description_12` (`description_12`), 
KEY `description_13` (`description_13`), 
KEY `description_14` (`description_14`), 
KEY `description_15` (`description_15`), 
KEY `description_16` (`description_16`), 
KEY `description_17` (`description_17`), 
KEY `description_18` (`description_18`), 
KEY `description_19` (`description_19`), 
KEY `description_20` (`description_20`), 
KEY `filename_10_2` (`filename_10`), 
KEY `filename_10_3` (`filename_10`) 
) ENGINE=MyISAM AUTO_INCREMENT=11174 DEFAULT CHARSET=latin1 

CREATE TABLE `dt_usersonline` (
`id` int(8) NOT NULL AUTO_INCREMENT, 
`timestamp` int(15) NOT NULL DEFAULT '0', 
`ip` varchar(40) NOT NULL DEFAULT '', 
`login` varchar(25) NOT NULL DEFAULT '', 
`userid` int(10) NOT NULL DEFAULT '0', 
`session_id` varchar(255) NOT NULL DEFAULT '', 
PRIMARY KEY (`id`), 
KEY `id_2` (`id`), 
KEY `timestamp` (`timestamp`), 
KEY `ip` (`ip`), 
KEY `login` (`login`), 
KEY `userid` (`userid`), 
KEY `session_id` (`session_id`) 
) ENGINE=MyISAM AUTO_INCREMENT=4424348 DEFAULT CHARSET=latin1 
+0

Прежде всего, вы должны запустить «EXPLAIN» на них (и, возможно, опубликовать результаты здесь). – watery

+0

, как сказано, запустите его через объяснение, затем сделайте 'show create table dt_members' и т.п. для каждой таблицы. Публикуйте эти данные. Мы возвращаемся и помогаем. – Drew

+0

@ BK435 жаль, что я вернусь, чтобы посмотреть, как получить полную информацию о создании таблицы. –

ответ

2
CREATE TABLE `dt_members` (
`id` int(11) NOT NULL AUTO_INCREMENT,... 

    CREATE TABLE `dt_photos` (
`id` int(11) NOT NULL AUTO_INCREMENT, 
`member_id` varchar(255) DEFAULT NULL,.. 

Вы присоединяетесь на ваш запрос как:

LEFT JOIN dt_photos AS ph ON m.id=ph.member_id

Обратите внимание, как каждый раз, когда вы присоединиться на member_id к m.id колонка выглядит как member_id int(11) DEFAULT NULL. Критерии присоединения должны соответствовать одному типу данных, в противном случае он выглядит так, как будто он выполняет полное сканирование таблицы. Он не использует ключ, потому что он считает, что он должен сканировать поле varchar, а не использовать индексы для предложения on.

Попробуйте Altering колонку member_id, что и m.id от dt_members

Интересно отметить, что вы на самом деле пытались создать ограничение внешнего ключа, он не позволил бы вам из-за несогласованных типов данных .. .

Предупреждение: таблица MYISAM будет заблокирована в течение всего процесса последующей таблицы.

+0

В подобной заметке поля 'login', в то время как оба являются совпадающими с varchar, не имеют уникальных ограничений в любой таблице. – Uueerdo

+0

Извините BK435, но я немного потерял то, что вы хотите, чтобы я попытался. его опоздать, и завтра я буду читать все. Вы хотите изменить TABLE dt_photos? или вы хотите изменить запрос? –

+0

, если есть способ изменить QUERY, я бы предпочел бы этот метод, иначе код, который записывается в эти таблицы, должен быть обновлен во многих местах. –

0

Похоже, большинство избирательности по вашему запросу на вашем dt_profile_approved таблице. Попробуйте создать составной индекс на (status, gender, lastlogin)

Это должно позволить выбирать и заказывать только индекс.

Ваш запрос делает печально известный шаблон SELECT a lot ORDER BY something DESC LIMIT tinynumber. Это дорого. Попробуйте отложить участие. Начните получать интересные элементы из dt_profile_approved таблицы, например:

    SELECT member_id, lastlogin 
        FROM dt_profile_approved 
        WHERE status=1 
        AND gender='Female' 
       ORDER BY lastlogin DESC 

Этот подзапрос может быть оптимизирован очень чисто с индексом соединения на (status, gender, lastlogin, member_id). Это называется индексом покрытия. Это имеет большое преимущество: дополнительная сортировка не требуется, поскольку индекс уже отсортирован.

На основании точного запроса, который вы нам указали, порядок в индексе gender и status не имеет значения. Но я предполагаю, что у вас есть еще один запрос, который ищет мужчин, и у вас может быть тот, который этого не замечает. Таким образом, статус, вероятно, будет более избирательным полем для всех ваших запросов. (Guessing.)

Затем присоедините этот подзапрос к остальной части вашего запроса ..., который будет выглядеть примерно так.

 SELECT DISTINCT 
      m.id AS memberID , m.login , m.age, 
      p.gender. p.name AS header , p.id AS profileID , 
      p.city , p.state , p.lastlogin, 
      o.login AS online , 
      c.name AS country , ph.filename_1 AS pic 
     FROM (
       SELECT member_id, lastlogin 
        FROM dt_profile_approved 
        WHERE status=1 
        AND gender='Female' 
       ORDER BY lastlogin DESC 
      ) AS sel 
INNER JOIN dt_profile_approved AS p ON sel.member_id = p.member_id 
INNER JOIN dt_members AS m ON m.id=sel.member_id 
    LEFT JOIN dt_privacy AS pv ON m.id=pv.member_id 
INNER JOIN dt_countries AS c ON c.id=p.country 
    LEFT JOIN dt_photos AS ph ON m.id=ph.member_id 
    LEFT JOIN dt_usersonline AS o ON m.login=o.login 
     WHERE (pv.unsearchable IS NULL OR pv.unsearchable='') 
     AND m.age BETWEEN 25 AND 40 
    ORDER BY sel.lastlogin DESC 
     LIMIT 0, 21; 

Если это работает, это будет связано с тем, что он может ограничить работу сортировки с использованием индекса.

0

Try перемещения, где условия в соответствующее условие соединения:

SELECT DISTINCT m.id AS memberID , m.login , m.age , p.gender 
, p.name AS header , p.id AS profileID , p.city , p.state , p.lastlogin 
, o.login AS online , c.name AS country , ph.filename_1 AS pic 
FROM dt_members AS m 
INNER JOIN dt_profile_approved AS p ON m.id=p.member_id 
    AND p.status=1 AND p.gender='Female' -- Moved from WHERE clause 
LEFT JOIN dt_privacy AS pv ON m.id=pv.member_id 
    AND (pv.unsearchable IS NULL OR pv.unsearchable='') -- Moved from WHERE clause 
INNER JOIN dt_countries AS c ON c.id=p.country 
LEFT JOIN dt_photos AS ph ON m.id=ph.member_id 
LEFT JOIN dt_usersonline AS o ON m.login=o.login 
WHERE m.age BETWEEN 25 AND 40 
ORDER BY p.lastlogin DESC 
LIMIT 0, 21; 

В то время, когда условия выполнены после все соединения, присоединитесь условия оцениваются во время соединения, так огромное количество ненужных объединений можно избежать ранней ,

Хотя теоретически оптимизатор запросов должен сделать это за вас, я обнаружил, что mysql может быть особенно плотным в этом отношении.

+1

Стоит отметить, что перемещение условия из 'WHERE' в' 'LEFT JOIN' ON' может немного изменить результат. В этом случае он получит результаты, даже если у них есть pv.unsearchable = 'x', он просто не увидит 'x'. – Uueerdo

Смежные вопросы