2013-10-14 7 views
0

У меня есть таблица данных больших (почти 10M записей), которая по соображениям производительности имеет вторичную таблицу сопутствующих агрегирования. Таблица агрегации регулярно заполняются ГНФАР неагрегированных данных:Как оптимизировать GROUP BY на вычисленном поле (использовать индекс)?

REPLACE INTO aggregate (channel_id, type, timestamp, value, count) 
SELECT channel_id, 'day' AS type, MAX(timestamp) AS timestamp, SUM(value) AS value, COUNT(timestamp) AS count FROM data 
WHERE timestamp < UNIX_TIMESTAMP(DATE_FORMAT(NOW(), "%Y-%m-%d")) * 1000 
AND timestamp >= IFNULL((SELECT UNIX_TIMESTAMP(DATE_ADD(FROM_UNIXTIME(MAX(timestamp)/1000, "%Y-%m-%d"), 
    INTERVAL 1 day)) * 1000 FROM aggregate WHERE type = 'day'), 0) 
GROUP BY channel_id, YEAR(FROM_UNIXTIME(timestamp/1000)), DAYOFYEAR(FROM_UNIXTIME(timestamp/1000)); 

Я обнаружил, что SELECT части заявления довольно медленно (2+ секунды на быстром компьютере), даже если данные не возвращается. Поскольку агрегация должна работать на встроенных устройствах, это вызывает озабоченность. Вот план:

id select_type table  type  key  key_len rows Extra 
1 PRIMARY  data  ALL       9184560 Using where; Using temporary; Using filesort 
2 SUBQUERY aggregate index  ts_uniq 22  1940 Using where; Using index 

Подзапрос сам по себе является мгновенным. По-видимому data не использует индекс channel_id/timestamp в связи с расчетом в пункте GROUP BY:

CREATE TABLE `data` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `channel_id` int(11) DEFAULT NULL, 
    `timestamp` bigint(20) NOT NULL, 
    `value` double NOT NULL, 
    PRIMARY KEY (`id`), 
    UNIQUE KEY `ts_uniq` (`channel_id`,`timestamp`), 
    KEY `IDX_ADF3F36372F5A1AA` (`channel_id`) 
) ENGINE=MyISAM AUTO_INCREMENT=10432870 DEFAULT CHARSET=latin1; 

Может запрос дополнительно оптимизированной?

Update: добавление запрашиваемой информация

SHOW INDEXES FROM data; 

Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Null Index_type 
data 0   PRIMARY  1    id   A   9184560    BTREE  
data 0   ts_uniq  1    channel_id A   164   YES  BTREE  
data 0   ts_uniq  2    timestamp A   9184560    BTREE  
data 1   IDX_ADF3.. 1    channel_id A   164   YES  BTREE  

CREATE TABLE `aggregate` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `channel_id` int(11) NOT NULL, 
    `type` varchar(8) NOT NULL, 
    `timestamp` bigint(20) NOT NULL, 
    `value` double NOT NULL, 
    `count` int(11) NOT NULL, 
    PRIMARY KEY (`id`), 
    UNIQUE KEY `ts_uniq` (`channel_id`,`type`,`timestamp`) 
) ENGINE=MyISAM AUTO_INCREMENT=1941 DEFAULT CHARSET=latin1; 

Я также заметил, что запрос будет мгновенным при изменении GROUP BY для CHANNEL_ID, метки времени. К сожалению, добавление вычислений данных в виде столбцов нежелательно, поскольку группировка динамически вычисляется.

Я не понимаю, почему индекс GROUP BY должен быть такой проблемой, когда нет никаких данных для группировки. Я попытался запустить

SELECT channel_id, 'day' AS type, MAX(timestamp) AS timestamp, SUM(value) AS value, COUNT(timestamp) AS count FROM data 
WHERE timestamp < UNIX_TIMESTAMP(DATE_FORMAT(NOW(), "%Y-%m-%d")) * 1000 
AND timestamp >= IFNULL((SELECT UNIX_TIMESTAMP(DATE_ADD(FROM_UNIXTIME(MAX(timestamp)/1000, "%Y-%m-%d"), INTERVAL 1 day)) * 1000 
    FROM aggregate WHERE type = 'day'), 0) 

, который так же, как медленно, так что GROUP, кажется, не проблема?

Update 2

Копаем дальше по этому пути, показывает, что

SELECT channel_id, 'day' AS type, timestamp, value, 1 FROM data 
WHERE timestamp >= (SELECT UNIX_TIMESTAMP(DATE_ADD(FROM_UNIXTIME(MAX(timestamp)/1000, "%Y-%m-%d"), 
    INTERVAL 1 day)) * 1000 FROM aggregate WHERE type = 'day'); 

по-прежнему медленно (1.4sec) - так не GROUP BY проблема.

Update 3

И это по-прежнему медленно:

SELECT channel_id, 'day' AS type, timestamp, value, 1 FROM data WHERE timestamp >= 1380837600000; 

Так что- проблема заключается в том, что внутреннее сравнение для временной метки, которые не могут сделать использование CHANNEL_ID, индекс метки времени, хотя это часть предложения GROUP BY. Что приводит к вопросу о том, как заставить этот индекс?

+0

Не могли бы вы также вставить таблицу 'aggragate'? – VancleiP

+0

Кроме того, вы можете проверить, правильно ли используются все индексы с помощью команды SHOW INDEXES FROM data; ... У меня есть подозрение в уникальном ключе ('channel_id',' timestamp') ... – VancleiP

+0

Можете ли вы попытаться заменить «GROUP BY channel_id, YEAR (FROM_UNIXTIME (timestamp/1000)), DAYOFYEAR (FROM_UNIXTIME (отметка времени/1000));' с 'GROUP BY channel_id, DATE (FROM_UNIXTIME (отметка времени/1000));' –

ответ

1

Добавьте столбец года и дня в таблицу данных и укажите индекс (channel_id, year, dayofyear). Заполните два новых столбца при вставке строки.

+1

К сожалению, не вариант - и, видимо, не проблема. – andig

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