代码之家  ›  专栏  ›  技术社区  ›  Martin AJ

如何在庞大的数据集上快速执行COUNT(*)?

  •  1
  • Martin AJ  · 技术社区  · 9 月前

    我有以下疑问:

    select count(*) as aggregate from users where date(created_at) < '2024-04-01';
    

    它返回 2337355 这是一个正确的数字。但它接管了 1.5 秒。知道我该如何使它更优化、更快吗?


    注意到我有一个想法 users(created_at, type) .

    还尝试删除 date() 功能类似 where created_at < '2024-04-01'; 但仍然很慢。

    2 回复  |  直到 9 月前
        1
  •  1
  •   Bill Karwin    9 月前

    COUNT() 查询往往比人们想象的要贵。它们的开销与检查数百万行的任何其他查询差不多。

    有些人认为是因为 计数() 返回一个整数结果,这个结果很小,所以查询必须比获取所有数据更快。但事实并非如此。

    查询的成本大致与检查的行数成比例,而不是与结果的大小成比例。

    要计算行数,InnoDB必须 检查 每行,因为MVCC。使用索引有助于减少检查的行数,但这可能仍需要检查数百万行。

    因此,它的性能成本与为其他目的读取相同行数的查询几乎相同。当然,将单个整数传递回客户端比返回数百万行更快,但网络传输速度并不是瓶颈。

    加快查询速度的解决方法包括:

    • 缓存代价高昂的查询的结果,这样您就不必经常运行它了。

    • 缓存部分结果,如每日计数,然后将您感兴趣的某个日期范围的计数相加。这需要检查更少的行(例如,每天一行),因此速度应该快得多。

    • 获得更快的服务器硬件,并为缓冲池提供更多的RAM。它可能仍然需要检查数百万行,但在某种程度上,它在更强大的服务器上检查得更快,它会更快地给出结果。它不太可能将性能提高几个数量级,但至少可以给你一个百分比的提高。

        2
  •  -2
  •   Bernd Buffen    9 月前

    如果可能,永远不要在字段的WHERE中使用函数。然后MySQL必须读取每一行,调用函数,然后才能与VALUE进行比较。这意味着这将始终是一个完整的表扫描,因此不能使用索引。

    这可能更明智,例如:

    WHERE created_at BETWEEN '2024-04-01 00:00:00' AND '2024-04-01 23:59:59';
    

    使用