设置:
db.t3.xlarge
RDS 实例。- MySQL 8.0.33 引擎。
- 默认参数和选项组。
- ~900k 记录表。
- RDS 根本没有启用日志记录,我们现在无法重新启动实例来启用它。
该数据库连接到在 Lambda 中运行的 Laravel 应用程序。lambda 中的过程之一需要计算表中的行数spin
。
在第一次迭代期间,我们发现select count()
以下面的方式编写的内容有时会花费大量的秒数。有些请求是立即发出的,而另一些请求可能需要 20 秒以上。
select count(*) from spin;
我们在互联网上挖掘了一些抱怨此事的人的答案。我们决定向查询添加一个条件,这使其成为亚秒级查询:
select count(*) from spin where id > 0;
直到几天前,我们的服务开始收到比平时更多的流量,并且查询运行时间变得非常跳跃。
+--------+-------+------------------+---------+---------+------+-----------+----------------------------------------------------------+
| ID | USER | HOST | DB | COMMAND | TIME | STATE | INFO |
+--------+-------+------------------+---------+---------+------+-----------+----------------------------------------------------------+
| 114168 | vmx | 10.0.2.175:43169 | fdata | Execute | 60 | executing | select count(*) as aggregate from `spin` where `id` > 0 |
| 114171 | vmx | 10.0.3.149:31136 | fdata | Execute | 58 | executing | select count(*) as aggregate from `spin` where `id` > 0 |
| 114118 | vmx | 10.0.2.175:36571 | fdata | Execute | 109 | executing | select count(*) as aggregate from `spin` where `id` > 0 |
+--------+-------+------------------+---------+---------+------+-----------+----------------------------------------------------------+
我怀疑这一定是由于对表的某些访问锁定造成的spin
。在表锁定期间,select count()
挂起。
任何意见表示赞赏,谢谢。
为什么要在近百万行的表中实时查询整个表的计数?这听起来有点武断——谁会注意到或关心这一秒的确切计数是 957,432,几分钟后现在是 957,433?
我至少建议缓存该计数,然后从缓存中读取重复命中。
不确定您如何在应用程序中利用这些计数,但您可以查询
INFORMATION_SCHEMA
以获得近似计数,它们会随着表统计信息的更新而更新,应该接近实际的行计数。原因是其他连接有时会以与 COUNT 冲突的方式接触该表。或者至少让它陷入困境。
我同意 JD 的观点,即寻找计数可能是不必要的。我同意 SD 的说法,你可以得到一个估计;然而,这一估计可能相差甚远。
请解释您的计数用途;也许我们可以定制一些技巧来帮助你。
一个技巧是有一个辅助列
INDEX(foo)
,其中foo
是表中最小的列之一。`COUNT(*) 将使用该 BTree 进行计数。这可能会跑得更快,并且可能会更少遇到减速带。