У меня есть таблица данных больших (почти 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
. Что приводит к вопросу о том, как заставить этот индекс?
Не могли бы вы также вставить таблицу 'aggragate'? – VancleiP
Кроме того, вы можете проверить, правильно ли используются все индексы с помощью команды SHOW INDEXES FROM data; ... У меня есть подозрение в уникальном ключе ('channel_id',' timestamp') ... – VancleiP
Можете ли вы попытаться заменить «GROUP BY channel_id, YEAR (FROM_UNIXTIME (timestamp/1000)), DAYOFYEAR (FROM_UNIXTIME (отметка времени/1000));' с 'GROUP BY channel_id, DATE (FROM_UNIXTIME (отметка времени/1000));' –