首页 > 解决方案 > 在大型 MySQL InnoDB 表上,全计数查询真的这么慢吗?

问题描述

我们有一个包含数百万条目的大表。完整计数非常慢,请参见下面的代码。这对于 MySQL InnoDB 表来说很常见吗?没有办法加速吗?即使使用查询缓存,它仍然“慢”。我还想知道,为什么具有 2.8 个 mio 条目的“通信”表的计数比具有 4.5 个 mio 条目的“事务”表的计数慢。

我知道使用 where 子句要快得多。我只是想知道表现不佳是否正常。

我们正在使用带有 m4.xlarge(4 CPU、16 GB RAM、500 GB 存储)的 Amazon RDS MySQL 5.7。我也已经尝试过具有更多 CPU 和 RAM 的更大实例,但查询时间没有太大变化。

mysql> SELECT COUNT(*) FROM transaction;
+----------+
| COUNT(*) |
+----------+
|  4569880 |
+----------+
1 row in set (1 min 37.88 sec)

mysql> SELECT COUNT(*) FROM transaction;
+----------+
| count(*) |
+----------+
|  4569880 |
+----------+
1 row in set (1.44 sec)

mysql> SELECT COUNT(*) FROM communication;
+----------+
| count(*) |
+----------+
|  2821486 |
+----------+
1 row in set (2 min 19.28 sec)

标签: mysqlperformanceinnodbmysql-slow-query-log

解决方案


这是使用支持多版本并发控制 (MVCC)的数据库存储引擎的缺点。

InnoDB 允许在事务中隔离您的查询,而不会阻塞正在读取和写入数据行的其他并发客户端。这些并发更新不会影响您的事务拥有的数据视图。

但是,鉴于在您进行计数时许多行正在被添加或删除,所以表中的行数是多少?答案是模糊的。

您的事务不应该能够“看到”在事务开始后创建的行版本。同样,即使其他人要求删除它们,您的事务也应该计算行数,但他们是在您的事务开始后才这样做的。

答案是当你做一个SELECT COUNT(*)——或任何其他类型的查询需要检查很多行——InnoDB 必须访问每一行,查看该行的当前版本对你的事务的数据库视图可见,并计数如果它是可见的。

在不支持事务或并发更新的表(如 MyISAM)中,存储引擎将总行数作为表的元数据保存。这个存储引擎不能支持多个线程同时更新行,所以行的总数不那么模糊。因此,当您从 MyISAM 表请求时SELECT COUNT(*),它只返回它在内存中的行数(但如果您SELECT COUNT(*)使用 WHERE 子句按某些条件计算某些行的子集,这将没有用,因此它必须实际计数他们在那种情况下)。

总的来说,大多数人觉得 InnoDB 对并发更新的支持很有价值,他们愿意牺牲SELECT COUNT(*).


推荐阅读