在大型 MySQL InnoDB 表上进行全计数查询真的那么慢吗?

Cra*_*ite 4 mysql performance innodb mysql-slow-query-log

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

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

我们使用 Amazon RDS MySQL 5.7 和 m4.xlarge(4 个 CPU、16 GB RAM、500 GB 存储)。我也已经尝试过使用更多 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)
Run Code Online (Sandbox Code Playgroud)

Bil*_*win 6

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

\n\n

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

\n\n

但是,考虑到在进行计数时许多行正在添加或删除,表中的行数是多少?答案是模糊的。

\n\n

您的事务不应能够“查看”事务启动后创建的行版本。同样,即使其他人请求删除行,您的事务也应该对行进行计数,但他们是在您的事务开始后这样做的。

\n\n

答案是,当您执行SELECT COUNT(*)\xe2\x80\x94\xc2\xa0 或任何其他类型的查询需要检查许多行 \xe2\x80\x94 InnoDB 必须访问每一行,以查看哪个是当前版本该行对数据库的事务视图可见,如果可见则对其进行计数。

\n\n

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

\n\n

总的来说,大多数人认为 InnoDB 对并发更新的支持非常值得,并且他们愿意牺牲SELECT COUNT(*).

\n