为啥 count(*) 查询在某些表上很慢,而在其他表上却没有?

Posted

技术标签:

【中文标题】为啥 count(*) 查询在某些表上很慢,而在其他表上却没有?【英文标题】:Why is count(*) query slow on some tables but not on others?为什么 count(*) 查询在某些表上很慢,而在其他表上却没有? 【发布时间】:2015-05-05 21:45:21 【问题描述】:

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

select count(*) from image;

在一个单独的表“概念”中,我存储了用户为其图像提供的标签列表。对“概念”表的类似查询耗时 0.8 秒。奥秘在于这两个表都有大约 200,000 行。 select count(*) from image; 返回 283,890,select count(*) from concept; 返回 213,357。

这是每个表的描述

显然,“图像”表的行数更大。我认为基于this blog post 可能“图像”太大而无法保存在内存中,因此我还使用this answer 中的代码测试了表的大小。

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,因此这两个表应该很容易适应。我错过了什么会减慢我的查询速度?我该如何解决?

【问题讨论】:

两张表使用的是什么存储引擎?是imageinnoDB 吗? 如果你改为使用 count(id) 会改变执行所需的时间吗? @Interrobang 是的,两者都是 innoDB。这是“图像”的错误选择吗? @Aziz 我尝试使用 count(id),“图像”花了 3 分 13 秒,所以只好 30 秒。 @2cents 有趣!然后我什么都没得到:) 【参考方案1】:

在对 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

【讨论】:

如果添加二级索引会有一些折衷。更新需要更长的时间,但 SELECT COUNT(*) 会更快。要进一步改进,请使用较小的主键,因为二级索引中的每条记录都有索引列和主键。 于是我把主键换成了自增整数主键,结果超级快,0.05秒。谢谢! 现在,您明白为什么我提到将 AI PK 值作为表中的第一列,无论您的 varchar 字段是否唯一。恕我直言,这是一种很好的做法,除非你能证明,否则将 AI PK 值作为所有表格的第一列。【参考方案2】:

如果您正在寻找一个大致数字而不是精确计数,那么 show table status 中的 Rows 列可能就足够了。对于 InnoDB 表而言,它并不总是准确的,但无论如何粗略估计您似乎都可以接受。

【讨论】:

以上是关于为啥 count(*) 查询在某些表上很慢,而在其他表上却没有?的主要内容,如果未能解决你的问题,请参考以下文章

为啥在主表上查询很慢?

带有子查询的 CTE 查询在小型索引表上很慢;如何在 MySQL 上进行优化?

为啥'=='在std :: string上很慢?

为啥 kafka 生产者在第一条消息上很慢?

MySQL简单插入查询在“查询结束”步骤上很慢

在左连接表上使用 WHERE OR 时查询很慢?