我们有一张有数百万入口的大桌子。完整的计数非常慢,请参阅下面的代码。对于MySQL InnoDB表来说,这种情况很常见吗?没有办法加速这件事吗?即使使用查询缓存,它仍然“慢”。我还想知道,为什么2.8百万条目的“通信”表的计数要比对4.5百万条目的"transaction“计数要慢。
我知道用where子句更快。我只想知道糟糕的表现是否正常。
我们使用的是Amazon 5.7和m4.xsize (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)发布于 2019-09-13 16:52:44
除了比尔说的..。
最小指数
InnoDB为执行COUNT(*)选择了最小的索引。结果表明,communication的各项指标均大于transaction的最小值,因此存在时间差。在判断索引的大小时,使用任何二级索引包括PRIMARY KEY列:
PRIMARY KEY(id), -- INT (4 bytes)
INDEX(flag), -- TINYINT (1 byte)
INDEX(name), -- VARCHAR(255) (? bytes)为了测量大小,PRIMARY KEY很大,因为它包含(由于集群)表的所有列。INDEX(flag)是"5字节“。INDEX(name)可能平均有几十个字节。SELECT COUNT(*)显然会选择INDEX(flag)。
显然,transaction有一个“小”指数,但communication没有。
TEXT/BLOG列有时被存储为“非记录”。因此,它们不计算PK索引的大小。
查询缓存
如果打开“查询缓存”,则查询的第二次运行可能比第一次快得多。但这只是在同一时间内没有改变表的情况。由于对表的任何更改都会使该表的所有QC条目无效,因此QC在生产系统中很少有用。我所说的“更快”指的是0.001秒,而不是1.44秒。
1m38s和1.44s之间的差异可能是由于buffer_pool中缓存的内容-- InnoDB的通用缓存区域。第一次运行可能没有发现RAM中最小的索引,所以它做了大量的I/O操作,花费了98秒来获取该索引的所有450万行。第二次运行发现所有缓存在buffer_pool中的数据,因此它以CPU速度运行(无I/O),因此运行速度要快得多。
足够好的
在这种情况下,我质疑做COUNT(*)的必要性。请注意您是如何说"2.8百万条目“的,就好像两个重要的数字”足够好“一样。如果您在UI上向用户显示计数,这难道不是“足够好”吗?如果是这样的话,性能的一个解决方案是每天进行一次计数,并将其存储在某个地方。这将允许即时访问“足够好”的值。
还有其他的技巧。一种是使用活动代码或使用某种形式的汇总表保持计数器的更新。
向it抛出硬件
您已经发现更改硬件没有帮助。
SELECT COUNT(*).. (内存太少会导致第二个运行非常慢)。发布于 2019-09-13 15:06:09
这是使用支持多版本并发控制(MVCC)的数据库存储引擎的缺点。
InnoDB允许在事务中隔离查询,而不阻塞正在读取和写入数据行的其他并发客户端。这些并发更新不会影响您的事务所拥有的数据视图。
但是,考虑到在进行计数时,许多行正在被添加或删除,那么表中的行数是多少?答案是模糊的。
您的事务不应该能够“看到”在您的事务启动后创建的行版本。同样,即使有人请求删除行,您的事务也应该对行进行计数,但它们是在事务启动后进行的。
答案是,当您执行SELECT COUNT(*) - or时,需要检查许多行的任何其他类型的查询-- InnoDB必须访问每一行,以查看该行的当前版本在您的事务的数据库视图中是可见的,并计算它是否可见。
在不支持事务或并发更新(如MyISAM )的表中,存储引擎将行总数保持为表的元数据。此存储引擎不能同时支持多个线程更新行,因此行总数不太模糊。因此,当您从一个SELECT COUNT(*)表请求MyISAM时,它只返回它在内存中拥有的行的计数(但是如果您使用WHERE子句执行SELECT COUNT(*)来根据某些条件来计数某些行子集,那么这是没有用的,所以在这种情况下它必须实际计数它们)。
一般来说,大多数人认为InnoDB对并发更新的支持很有价值,他们愿意牺牲SELECT COUNT(*)的优化。
https://stackoverflow.com/questions/57921400
复制相似问题