29

我有一个较大但很窄的 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;

虽然该语句不经常运行,但对其进行优化会很好。根据http://www.cloudspace.com/blog/2009/08/06/fast-mysql-innodb-count-really-fast/这应该可以通过强制 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

不幸的是,声明和以前一样慢。根据“SELECT COUNT(*)”的速度很慢,即使使用 where 子句,我也尝试过优化表但没有成功。

什么/是/重新优化COUNT(*)InnoDB 性能的方法?

4

3 回答 3

22

从 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 或队列),可以轻松定制以根据计数所需的新鲜度运行。

于 2013-12-19T03:57:50.037 回答
18

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

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

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

于 2013-10-09T18:44:36.710 回答
17

基于@Che 代码,您还可以使用触发器 onINSERT和 on UPDATEtoperf2以使统计表中的值实时更新。

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

然后:

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因此,可以使用此查询实时读取表中的行数:

SELECT `value` FROM `stats` WHERE `key` = 'perf2_count';

这将具有消除执行 a 的性能问题的优势,COUNT(*)并且仅在perf2.

于 2013-12-19T04:38:20.603 回答