mysql - 在大型 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)
解决方案
这是使用支持多版本并发控制 (MVCC)的数据库存储引擎的缺点。
InnoDB 允许在事务中隔离您的查询,而不会阻塞正在读取和写入数据行的其他并发客户端。这些并发更新不会影响您的事务拥有的数据视图。
但是,鉴于在您进行计数时许多行正在被添加或删除,所以表中的行数是多少?答案是模糊的。
您的事务不应该能够“看到”在事务开始后创建的行版本。同样,即使其他人要求删除它们,您的事务也应该计算行数,但他们是在您的事务开始后才这样做的。
答案是当你做一个SELECT COUNT(*)
——或任何其他类型的查询需要检查很多行——InnoDB 必须访问每一行,查看该行的当前版本对你的事务的数据库视图可见,并计数如果它是可见的。
在不支持事务或并发更新的表(如 MyISAM)中,存储引擎将总行数作为表的元数据保存。这个存储引擎不能支持多个线程同时更新行,所以行的总数不那么模糊。因此,当您从 MyISAM 表请求时SELECT COUNT(*)
,它只返回它在内存中的行数(但如果您SELECT COUNT(*)
使用 WHERE 子句按某些条件计算某些行的子集,这将没有用,因此它必须实际计数他们在那种情况下)。
总的来说,大多数人觉得 InnoDB 对并发更新的支持很有价值,他们愿意牺牲SELECT COUNT(*)
.
推荐阅读
- c# - 更新对象属性在可枚举中不起作用
- git - SourceTree、gitlab 和 TeamCity 之间的持续身份验证问题(涉及 gitLFS)
- c# - 使用 C# SDK for IIS,有没有办法查询 Web 服务并检测它有哪些方法?
- django - 不知道 SpatiaLite 是什么,以及如何获得它
- javascript - 包含函数的 JS 代码到流程图
- mediawiki - MobileFrontend 破坏了 mediawiki 主页
- c - 调试步骤以相反的顺序跳转
- r - 如何用分组计算百分位数?
- json - 参数类型'列表
?不能分配给参数类型“列表” '。使用列表时 - php - 填充 CodeIgniter MVC 表时为 foreach() 提供的参数无效