У нас есть таблица в MySql с примерно 30 миллионами записей, следующая структура таблицы
CREATE TABLE `campaign_logs` (
`domain` varchar(50) DEFAULT NULL,
`campaign_id` varchar(50) DEFAULT NULL,
`subscriber_id` varchar(50) DEFAULT NULL,
`message` varchar(21000) DEFAULT NULL,
`log_time` datetime DEFAULT NULL,
`log_type` varchar(50) DEFAULT NULL,
`level` varchar(50) DEFAULT NULL,
`campaign_name` varchar(500) DEFAULT NULL,
KEY `subscriber_id_index` (`subscriber_id`),
KEY `log_type_index` (`log_type`),
KEY `log_time_index` (`log_time`),
KEY `campid_domain_logtype_logtime_subid_index` (`campaign_id`,`domain`,`log_type`,`log_time`,`subscriber_id`),
KEY `domain_logtype_logtime_index` (`domain`,`log_type`,`log_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 |
Ниже приведен мой запрос
Я делаю UNION ALL вместо операции IN
SELECT log_type,
DATE_FORMAT(CONVERT_TZ(log_time,'+00:00','+05:30'),'%l %p') AS log_date,
count(DISTINCT subscriber_id) AS COUNT,
COUNT(subscriber_id) AS total
FROM stats.campaign_logs USE INDEX(campid_domain_logtype_logtime_subid_index)
WHERE DOMAIN='xxx'
AND campaign_id='123'
AND log_type = 'EMAIL_OPENED'
AND log_time BETWEEN CONVERT_TZ('2015-02-01 00:00:00','+00:00','+05:30') AND CONVERT_TZ('2015-03-01 23:59:58','+00:00','+05:30')
GROUP BY log_date
UNION ALL
SELECT log_type,
DATE_FORMAT(CONVERT_TZ(log_time,'+00:00','+05:30'),'%l %p') AS log_date,
COUNT(DISTINCT subscriber_id) AS COUNT,
COUNT(subscriber_id) AS total
FROM stats.campaign_logs USE INDEX(campid_domain_logtype_logtime_subid_index)
WHERE DOMAIN='xxx'
AND campaign_id='123'
AND log_type = 'EMAIL_SENT'
AND log_time BETWEEN CONVERT_TZ('2015-02-01 00:00:00','+00:00','+05:30') AND CONVERT_TZ('2015-03-01 23:59:58','+00:00','+05:30')
GROUP BY log_date
UNION ALL
SELECT log_type,
DATE_FORMAT(CONVERT_TZ(log_time,'+00:00','+05:30'),'%l %p') AS log_date,
COUNT(DISTINCT subscriber_id) AS COUNT,
COUNT(subscriber_id) AS total
FROM stats.campaign_logs USE INDEX(campid_domain_logtype_logtime_subid_index)
WHERE DOMAIN='xxx'
AND campaign_id='123'
AND log_type = 'EMAIL_CLICKED'
AND log_time BETWEEN CONVERT_TZ('2015-02-01 00:00:00','+00:00','+05:30') AND CONVERT_TZ('2015-03-01 23:59:58','+00:00','+05:30')
GROUP BY log_date,
Ниже приводится мое объяснение.
+----+--------------+---------------+-------+-------------------------------------------+-------------------------------------------+---------+------+--------+------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+--------------+---------------+-------+-------------------------------------------+-------------------------------------------+---------+------+--------+------------------------------------------+
| 1 | PRIMARY | campaign_logs | range | campid_domain_logtype_logtime_subid_index | campid_domain_logtype_logtime_subid_index | 468 | NULL | 55074 | Using where; Using index; Using filesort |
| 2 | UNION | campaign_logs | range | campid_domain_logtype_logtime_subid_index | campid_domain_logtype_logtime_subid_index | 468 | NULL | 330578 | Using where; Using index; Using filesort |
| 3 | UNION | campaign_logs | range | campid_domain_logtype_logtime_subid_index | campid_domain_logtype_logtime_subid_index | 468 | NULL | 1589 | Using where; Using index; Using filesort |
| NULL | UNION RESULT | <union1,2,3> | ALL | NULL | NULL | NULL | NULL | NULL | |
+----+--------------+---------------+-------+-------------------------------------------+-------------------------------------------+---------+------+--------+------------------------------------------+
- Я изменил COUNT(subscriber_id) на COUNT(*) и не заметил повышения производительности.
2. Я удалил COUNT (DISTINCT subscriber_id) из запроса, после чего получил огромный прирост производительности, я получаю результаты примерно через 1,5 секунды, раньше это занимало от 50 секунд до 1 минуты. Но мне нужно отдельное количество subscriber_id из запроса
Ниже объясняется, когда я удаляю COUNT(DISTINCT subscriber_id) из запроса
+----+--------------+---------------+-------+-------------------------------------------+-------------------------------------------+---------+------+--------+-----------------------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+--------------+---------------+-------+-------------------------------------------+-------------------------------------------+---------+------+--------+-----------------------------------------------------------+
| 1 | PRIMARY | campaign_logs | range | campid_domain_logtype_logtime_subid_index | campid_domain_logtype_logtime_subid_index | 468 | NULL | 55074 | Using where; Using index; Using temporary; Using filesort |
| 2 | UNION | campaign_logs | range | campid_domain_logtype_logtime_subid_index | campid_domain_logtype_logtime_subid_index | 468 | NULL | 330578 | Using where; Using index; Using temporary; Using filesort |
| 3 | UNION | campaign_logs | range | campid_domain_logtype_logtime_subid_index | campid_domain_logtype_logtime_subid_index | 468 | NULL | 1589 | Using where; Using index; Using temporary; Using filesort |
| NULL | UNION RESULT | <union1,2,3> | ALL | NULL | NULL | NULL | NULL | NULL | |
+----+--------------+---------------+-------+-------------------------------------------+-------------------------------------------+---------+------+--------+-----------------------------------------------------------+
- Я выполнил три запроса по отдельности, удалив UNION ALL. ОДИН запрос занял 32 секунды, другие занимают 1,5 секунды каждый, но первый запрос обрабатывает около 350 тысяч записей, а другие имеют дело только с 2 тысячами строк.
Я мог бы решить свою проблему с производительностью, опустив COUNT(DISTINCT...)
, но мне нужны эти значения. Есть ли способ реорганизовать мой запрос или добавить индекс или что-то еще, чтобы получить значения COUNT(DISTINCT...)
, но намного быстрее?
ОБНОВЛЕНИЕ следующая информация касается распределения данных в приведенной выше таблице.
на 1 домен 1 кампания 20 log_types 1к-200к подписчиков
Приведенный выше запрос я выполняю для домена с более чем 180 000 подписчиков.
AND (log_type = 'EMAIL_OPENED' OR log_type = 'EMAIL_SENT' OR log_type = 'EMAIL_CLICKED')
- person Tschallacka   schedule 16.03.2015ORDER BY NULL
после каждогоGROUP BY
, это может избавиться от файловой сортировки. - person GarethD   schedule 16.03.2015EXPLAIN
дает понять, что ваш составной индекс используется по назначению. Вот что можно попробовать: 1) изменитьCOUNT(subscriber_id)
наCOUNT(*)
и посмотреть, улучшится ли производительность. 2) попробуйте избавиться отCOUNT(DISTINCT subscriber_id)
и посмотрите, улучшится ли производительность. Запустите каждый из трех подзапросов, которые вы комбинируете сUNION ALL
, и посмотрите, не уступает ли один из них производительности по сравнению с двумя другими. Пожалуйста, обновите свой вопрос с результатами этих тестов. - person O. Jones   schedule 16.03.2015GROUP BY HOUR( CONVERT_TZ(log_time,'+00:00','+05:30'))
вместо столбца набора результатов log_date в формате%l %p
. Числовая группировка может облегчить обработку. - person O. Jones   schedule 16.03.2015DISTINCT
замедляет работу. - person O. Jones   schedule 17.03.2015DISTINCT
: дляGROUP
их, движок сортирует 350 тысяч строк по результату функцииDATE_FORMAT
, а затем проходит по результату сортировки и подсчитывает строки в любом порядке их появления. Когда вы добавляетеDISTINCT
, движок должен снова сортировать внутри каждой группы. Вид вложенной сортировки. Судя по всему, с этим справляются неэффективно. - person Vladimir Baranov   schedule 19.03.2015COUNT(DISTINCT subscriber_id)
будет меньше примерно на 1 %? Если это так (и вы исправитеBETWEEN
), я могу объяснить, как пересмотреть запрос (и другие вещи), чтобы ускорить его как минимум в 100 раз. - person Rick James   schedule 20.03.2015IN
(то есть безunion
) и удалитеcount(distinct)
? - person Gordon Linoff   schedule 21.03.2015