在MySQL中,SELECT count(*)比SELECT count(*), some_column慢两倍。

4
我有两个简单的查询:
  1. SELECT count(*) FROM table1 WHERE cond1=exp1 AND cond2=exp2

    返回行数。例如,47。

  2. SELECT count(*),some_column FROM table1 WHERE cond1=exp1 AND cond2=exp2

    返回行数(例如,47),和一个来自some_column的任意值,而且它比第一个查询快两倍!我在服务器上直接测试了它,当然是通过 GUI(HeidiSQL),结果相同,查询2快两倍!

为什么会这样呢?有任何想法吗?

我使用的是 MySQL 5.6.21 和 Win Server 2012 上的 Apache/PHP 5.3。

更新1:

CREATE TABLE `programs` (
    `tvp_id` INT(11) NOT NULL AUTO_INCREMENT,
    `tvp_time` TIME NOT NULL DEFAULT '00:00:00',
    `tvp_time_end` TIME NOT NULL DEFAULT '00:00:00',
    `tvp_date` DATE NOT NULL DEFAULT '0000-00-00',
    `tvp_title` VARCHAR(200) NOT NULL,
    `tvp_channel` INT(11) NOT NULL DEFAULT '0',
    `tvp_type` VARCHAR(20) NOT NULL,
    `tvp_description` TEXT NOT NULL',

    ... more and more columns ...

    PRIMARY KEY (`tvp_id`),
    INDEX `tvp_date` (`tvp_date`),
    INDEX `tvp_channel` (`tvp_channel`),
    INDEX `tvp_time` (`tvp_time`),
)
ENGINE=MyISAM

我清除了缓存并多次运行查询 - 结果是相同的 - 2) 查询速度提升了2倍。

我的表格的具体查询如下:

1.  SELECT COUNT(*)            FROM programs WHERE (tvp_chanel = value_channel) AND (tvp_date = value_date)

或者

2.  SELECT COUNT(*), tvp_type FROM programs WHERE (tvp_channel = value_channel) AND (tvp_date = value_date)

所以WHERE子句中的两个列都是带有索引的列。

我尝试对此查询进行EXPLAIN:

1. "id" "select_type"   "table" "type"  "possible_keys" "key"   "key_len"   "ref"   "rows"  "Extra"
"1" "SIMPLE"    "programs"  "index_merge"   "tvp_date,tvp_channel"  "tvp_channel,tvp_date"  "4,3"   \N  "15"    "Using intersect(tvp_channel,tvp_date); Using where; Using index"

2. "id" "select_type"   "table" "type"  "possible_keys" "key"   "key_len"   "ref"   "rows"  "Extra"
"1" "SIMPLE"    "programms" "index_merge"   "tvp_date,tvp_channel"  "tvp_channel,tvp_date"  "4,3"   \N  "15"    "Using intersect(tvp_channel,tvp_date); Using where"

不同之处在于“使用索引”在1)查询中。那么为什么1)查询会更慢呢?


4
如果没有 GROUP BY some_column,第二个查询可能没有用。 - Michael Berkowski
添加表定义,我怀疑索引是答案。 - Mihai
1
@ChrisBint 嗯,我不知道 - 我猜MySQL可能有一些问题。我的意思是,它并不完美 - 否则为什么会存在替代产品呢?;-) - Strawberry
2
将两个查询的查询计划(即EXPLAIN 输出)添加到您的问题中可能是一个好主意。 - Andriy M
8
你需要在一个非常特定的环境中测试查询。可能的情况是你运行了第一个查询,用表格填满了内存,然后第二个查询不需要实际从磁盘读取。你需要清除缓存并多次运行查询,以获得近似准确的性能结果。 - Gordon Linoff
显示剩余7条评论
3个回答

2

不是对你问题的直接回答,但为什么不使用count(1)而不是count(*)呢?如Patrick所建议的那样,这可能是优化器选择要计数的列的问题。如果你使用count(1),则没有涉及任何列,只是返回记录数的计数。

(注意:已保留HTML标记)

0

这或许是一个猜测,但我可以想象优化器可能会不明智地选择全扫描一个非聚集索引(也许是主键),而第二个查询则在某种程度上促使优化器全扫描聚集索引(也就是没有索引,只是按照存储的行查看)。

或者也许不是。谁能说得准任何DBMS中优化器的方式呢?


0

我注意到你的表引擎是MyISAM,你有必须使用它的原因吗?无论如何,你能否将其转换为InnoDB并查看这两个查询的结果如何呢?

顺便说一句,这可能与你的问题无关,index merge并不总是好的,在某些情况下甚至可能成为性能杀手。请参阅此处的示例:http://www.percona.com/blog/2012/12/14/the-optimization-that-often-isnt-index-merge-intersection/

重点是,许多单列索引可能是一个危险信号。例如,如果你的查询是

SELECT COUNT(*) FROM programs WHERE (tvp_chanel = value_channel) AND (tvp_date = value_date),

(tvp_chanel, tvp_date)上建立索引是一个覆盖索引,这是最理想的情况。(是的,我知道这可能毕竟与问题无关,只是忍不住提一下)

而且,为了避免干扰测试,请在测试时使用select sql_no_cache禁用查询缓存。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接