J'ai une table InnoDB large mais étroite avec ~ 9m d'enregistrements. Faire count(*)
ou count(id)
sur la table est extrêmement lent (6+ secondes):
DROP TABLE IF EXISTS `perf2`;
CREATE TABLE `perf2` (
`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_CHANNEL_ID` (`channel_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
RESET QUERY CACHE;
SELECT COUNT(*) FROM perf2;
Bien que l'instruction ne soit pas exécutée trop souvent, il serait intéressant de l'optimiser. Selon http://www.cloudspace.com/blog/2009/08/06/fast-mysql-innodb-count-really-fast/ cela devrait être possible en forçant InnoDB à utiliser un index :
SELECT COUNT(id) FROM perf2 USE INDEX (PRIMARY);
Le plan d'explication semble correct:
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE perf2 index NULL PRIMARY 4 NULL 8906459 Using index
Malheureusement, la déclaration est aussi lente qu'auparavant. Selon "SELECT COUNT (*)" est lent, même avec la clause where J'ai également essayé d'optimiser la table sans succès.
Quel/est le/re un moyen d'optimiser les performances de COUNT(*)
sur InnoDB?
Pour l'instant, j'ai résolu le problème en utilisant cette approximation:
EXPLAIN SELECT COUNT(id) FROM data USE INDEX (PRIMARY)
Le nombre approximatif de lignes peut être lu dans la colonne rows
du plan d'explication lors de l'utilisation d'InnoDB comme indiqué ci-dessus. Lors de l'utilisation de MyISAM, cela restera VIDE car la référence de la table est optimisée à l'écart, donc si le repli vide vers le SELECT COUNT
Traditionnel à la place.
Depuis MySQL 5.1.6, vous pouvez utiliser Event Scheduler et insérer régulièrement le décompte dans un tableau de statistiques.
Créez d'abord une table pour contenir le décompte:
CREATE TABLE stats (
`key` varchar(50) NOT NULL PRIMARY KEY,
`value` varchar(100) NOT NULL);
Créez ensuite un événement pour mettre à jour la table:
CREATE EVENT update_stats
ON SCHEDULE
EVERY 5 MINUTE
DO
INSERT INTO stats (`key`, `value`)
VALUES ('data_count', (select count(id) from data))
ON DUPLICATE KEY UPDATE value=VALUES(value);
Ce n'est pas parfait, mais il offre une solution autonome (pas de cronjob ou de file d'attente) qui peut être facilement adaptée pour fonctionner aussi souvent que la fraîcheur requise du compte.
Basé sur le code @Che, vous pouvez également utiliser des déclencheurs sur INSERT et UPDATE to perf2 afin de maintenir la valeur dans le tableau des statistiques à jour.
CREATE TRIGGER `count_up` AFTER INSERT ON `perf2` FOR EACH ROW UPDATE `stats`
SET
`stats`.`value` = `stats`.`value` + 1
WHERE
`stats`.`key` = "perf2_count";
CREATE TRIGGER `count_down` AFTER DELETE ON `perf2` FOR EACH ROW UPDATE `stats`
SET
`stats`.`value` = `stats`.`value` - 1
WHERE
`stats`.`key` = "perf2_count";
Cela aurait l'avantage d'éliminer le problème de performances lié à l'exécution d'un comptage (*) et ne serait exécuté que lorsque les données changent dans la table perf2