如何使用索引优化InnoDB上的COUNT(*)性能

时间:2013-10-09 09:05:07

标签: mysql innodb

我有一个较大但很窄的InnoDB表,记录约为9m。在桌面上执行count(*)count(id)非常慢(6秒以上):

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;

虽然声明不经常运行,但优化它会很好。根据{{​​3}},这应该可以通过强制InnoDB使用索引来实现:

SELECT COUNT(id) FROM perf2 USE INDEX (PRIMARY);

解释计划似乎很好:

id  select_type table   type    possible_keys   key     key_len ref     rows    Extra
1   SIMPLE      perf2   index   NULL            PRIMARY 4       NULL    8906459 Using index

不幸的是,这句话和以前一样慢。根据{{​​3}},我也尝试过优化表格。

在InnoDB上优化COUNT(*)性能的方法是什么?

4 个答案:

答案 0 :(得分:17)

目前我已经通过使用这种近似解决了这个问题:

EXPLAIN SELECT COUNT(id) FROM data USE INDEX (PRIMARY)

使用InnoDB时,可以从解释计划的rows列中读取大致的行数,如上所示。当使用MyISAM时,这将保持EMPTY,因为表格参考正在被优化 - 所以如果空的回退到传统的SELECT COUNT

答案 1 :(得分:14)

从MySQL 5.1.6开始,您可以使用Event Scheduler并定期将统计数据插入统计数据表。

首先创建一个表来保存计数:

CREATE TABLE stats (
`key` varchar(50) NOT NULL PRIMARY KEY,
`value` varchar(100) NOT NULL);

然后创建一个事件来更新表:

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);

它并不完美,但它提供了一个独立的解决方案(没有cronjob或队列),可以轻松定制,以便按照计数所需的新鲜度运行。

答案 2 :(得分:12)

基于@Che代码,您还可以在INSERT和UPDATE上使用触发器来执行perf2,以使stats表中的值保持最新。

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";

这样做的好处是可以消除执行计数(*)的性能问题,并且只会在表 perf2

中的数据更改时执行

答案 3 :(得分:-4)

select max(id) - min(id) from xxx_table where ....

这将使用“选择经过优化的表”,并且速度非常快!

注意max(id) - min(id)实际上大于count(1)