为什么count(*)查询在某些表上缓慢而在其他表上没有?

时间:2015-03-04 18:40:52

标签: mysql wamp

我在wamp服务器上运行了一个mysql数据库,我用它来对Flickr数据进行频繁的模式挖掘。在将数据加载到数据库的过程中,我运行了一个计数查询来确定我已经加载了多少图像。

我花了3分49秒才感到惊讶
select count(*) from image;

在另一个表格" concept"中,我存储了用户提供图片的标签列表。关于"概念的类似查询"桌上花了0.8秒。神秘之处在于两个表都有大约200,000行。 select count(*) from image;返回283,890,select count(*) from concept;返回213,357。

这里是每个表格的描述

Screenshot of mysql console with table descriptions

显然"图像"表有更大的行。我想也许"图像"根据{{​​3}},内存太大而无法保存在内存中,因此我还使用this blog post中的代码测试了表的大小。

SELECT table_name AS "Tables", 
round(((data_length + index_length) / 1024 / 1024), 2) "Size in MB" 
FROM information_schema.TABLES 
WHERE table_schema = "$DB_NAME"
ORDER BY (data_length + index_length) DESC;

"图像"是179.98 MB,"概念"是15.45 MB

我在拥有64 GB RAM的计算机上运行mysql,因此这两个表都应该很容易适应。我错过了什么会减慢我的查询速度?我该如何解决?

2 个答案:

答案 0 :(得分:6)

在InnDB表上执行SELECT COUNT(*)时,MySQL必须扫描索引以计算行数。在这种情况下,您唯一的索引是主(聚集)索引,因此MySQL会扫描该索引。

对于聚簇索引,实际的表数据也存储在那里。不包括开销,您的image表每行约为1973字节(我假设两个主键列都有单字节字符集)。这是每(16k)页最多8条记录,因此大约35,486页。您的comcept表每行大约257个字节。那个页面大约有63个记录,所以大约有3,386页。这是必须扫描的数据量的巨大差异。

它必须完全读取每个页面,因为页面可能不完整。

然后,表现明智,也许这些页面中的一些是在内存中而有些则不是。由于MySQL的15/16偏好,也存在一些边际差异,但上述所有数字都应视为近似值。

<强>解决方案

向较大的表添加辅助索引应该会产生与较小的表大致相同的SELECT COUNT(*)性能。当然,要更新另一个索引,更新会慢一些。

为了提高性能,请缩短主键,因为二级索引包括索引列和完整主键。

如果您只需要估计的行数,则可以使用以下某个值中的rows值,该值使用表统计信息而不是扫描索引:

SHOW TABLE STATUS LIKE 'image'

EXPLAIN SELECT COUNT(*) FROM image

答案 1 :(得分:1)

如果你正在寻找一个球场号而不是一个确切的数,那么来自show table status的Rows列可能就足够了。 InnoDB表格并不总是准确的,但无论如何,您似乎还可以粗略估计。