2016-10-04 5 views
1

Я пытаюсь улучшить производительность следующего запроса, который занял 93,2 секунд, чтобы выполнить запрос ниже:MySQL Улучшение времени выполнения производительности

SELECT year(date), month(date), `country_name_name`, 
     CEIL(count(res.`user_xmpp_login`) /DAY(LAST_DAY(date))) as avgUser, 
     CEIL(count(res.user)/DAY(LAST_DAY(date))) as avgPurchase 
    FROM 
    ( SELECT DATE(`user_registration_timestamp`) as date, 
       user_country, 
       NULL as user, `user_xmpp_login` 
      FROM users 
      WHERE `user_registration_timestamp` >= "2015-01-01 00:00:00" 
       AND `user_registration_timestamp` < "2016-01-01 00:00:00" 
      UNION ALL 
     SELECT DATE(`ts`) as date, user_country, user, NULL as `user_xmpp_login` 
      FROM purchase_log p 
      INNER JOIN users u ON u.`user_xmpp_login` = p.`user` 
      WHERE `ts` >= "2015-01-01 00:00:00" 
       AND `ts` < "2016-01-01 00:00:00" 
       AND result in ('ok', 'cancelled', 'pending') 
    ) AS res 
    INNER JOIN countries c ON c.`country_id` = res.`user_country` 
    INNER JOIN country_names cn 
       ON (cn.`country_name_country` = c.`country_id` 
       AND cn.`country_name_language` = 'en') 
    GROUP BY 1,2,3 
    ORDER BY 4 DESC,5 DESC, 3 ASC; 

Объяснить команду показывает: enter image description here и структуру каждой таблицы:

покупки таблица:

CREATE TABLE `purchase` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `user` varchar(255) COLLATE utf8_unicode_ci NOT NULL, 
    `ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 
    `result` varchar(32) COLLATE utf8_unicode_ci NOT NULL, 
    PRIMARY KEY (`id`), 
    KEY `iuser` (`user`), 
) ENGINE=InnoDB AUTO_INCREMENT=12710221 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; 

пользователей таблица:

CREATE TABLE `users` (
    `user_id` int(11) NOT NULL AUTO_INCREMENT, 
    `user_country` int(11) DEFAULT NULL, 
    `user_xmpp_login` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, 
    `user_registration_timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 
    PRIMARY KEY (`user_id`), 
    UNIQUE KEY `user_xmpp_login_UNIQUE` (`user_xmpp_login`), 
    KEY `user_country_FK` (`user_country`), 
    KEY `user_registration_timestamp` (`user_registration_timestamp`), 
    CONSTRAINT `users_country_FK` FOREIGN KEY (`user_country`) 
     REFERENCES `countries` (`country_id`) ON UPDATE CASCADE 
) ENGINE=InnoDB AUTO_INCREMENT=33504745 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; 

страны таблица

CREATE TABLE `countries` (
    `country_id` int(11) NOT NULL AUTO_INCREMENT, 
    `country_code` varchar(2) COLLATE utf8_unicode_ci NOT NULL, 
    PRIMARY KEY (`country_id`), 
) ENGINE=InnoDB AUTO_INCREMENT=508 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; 

названия стран

CREATE TABLE `country_names` (
    `country_name_id` int(11) NOT NULL AUTO_INCREMENT, 
    `country_name_country` int(11) NOT NULL, 
    `country_name_language` char(2) COLLATE utf8_unicode_ci NOT NULL, 
    `country_name_name` varchar(255) COLLATE utf8_unicode_ci NOT NULL, 
    PRIMARY KEY (`country_name_id`), 
    UNIQUE KEY `country_name_country_language_UNIQUE` 
      (`country_name_country`,`country_name_language`), 
    KEY `country_name_language` (`country_name_language`), 
    CONSTRAINT `country_name_country` FOREIGN KEY (`country_name_country`) 
     REFERENCES `countries` (`country_id`) ON DELETE CASCADE ON UPDATE CASCADE 
) ENGINE=InnoDB AUTO_INCREMENT=45793 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; 

Есть ли какие-либо рекомендации?

+0

@ e4c5, я сделал! –

ответ

1

Если вы используете каждый подзапрос, я думаю, вы найдете users является самым медленным компонентом.

Подзапрос purchase_log можно, вероятно, улучшить с помощью этого «покрытия» INDEX(result, ts, user).

Объедините две столы «страны» !. Используйте CHAR(2) CHARACTER SET ascii для PRIMARY KEY и JOINs для других таблиц. Это всего 2 байта, в отличие от INT, который составляет 4 байта и VARCHAR..., что составляет 3 байта (в данном случае).

Вы упомянули ts, но я не вижу, откуда он. Если он находится в purchase_log, то для этой таблицы требуется INDEX(user, ts).

Какой процент от users задействован в 2015 году? Если это больше, чем около 20%, то INDEX(user_registration_timestamp) не поможет.

Рассмотрите: Избавьтесь от ПЕРВИЧНОГО КЛЮЧА (country_name_id) и продвиньте ключ UNIQUE до PRIMARY.

+0

thx, что означают ваши первые предложения, что делать, чтобы решить? У меня означает, что я не использую DATE ('user_registration_timestamp') в подзапросе, и вместо этого я сам использую поле? о объединении двух таблиц страны, и имею в виду, что я использую один стол, а не два? проблема здесь - country_names, потому что у некоторых стран есть более 2 переводов для их имени. Наконец, каково ваше решение для пользователей с числом пользователей более 20%, то есть больше, чем это. –

+0

'SELECT .. FROM users ..', вероятно, самая медленная часть; сначала убедитесь, что это самая медленная часть, а затем подумайте, можно ли ее каким-либо образом переписать. Изменение 'DATE (...)' не поможет. Много ли повторяющихся строк из запроса? –

+0

Уникальный идентификатор для страны может быть стандартным 2-буквенным кодом (не зависящим от языка), тем самым избегая INT и необходимость в первой из двух таблиц. –

1

Основная проблема, кажется, в вашей таблице users. Помните, что для большинства ситуаций mysql может использовать только один индекс для таблицы. В таблице ваших пользователей столбец user_xmpp_login_UNIQUE используется для присоединения к таблице purchase_log. Следовательно, индекс user_registration_timestamp не используется при сравнении с столбцом метки времени.

Одно из предложений заключается в создании составного индекса на столбцахи user_registration_timestamp.

+0

Спасибо 4 вашего ответа, позвольте мне проверить его с вашим предложением, и я скажу результат. –

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