【问题标题】:Improve performance of counting records for only growing table (without deletion)提高仅增长表的计数记录性能(不删除)
【发布时间】:2019-12-25 02:46:21
【问题描述】:

我已经阅读了很多关于计数记录性能的答案,例如https://stackoverflow.com/a/1332730/7906257,并且都说没有简单的解决方案。我使用 InnoDB 并且有 CL_GAME 表和 PK 主键。最重要的是我有一个特殊的用例,它绝对可以优化:记录永远不会从表中删除,而只会添加。是否可以说 MySQL 在计算记录时跳过内部验证(如上面链接中所述):

SELECT COUNT(pk) FROM CL_GAME WHERE pk <= 1072370;

有些想法并不完美:

  1. 一个明显的技巧是规范化表并消除pk之间的任何间隙。所以 pk 反映了之前的记录数。但这看起来很危险,因为某些还原的事务可能会破坏它(好吧,有一些不平凡的解决方案,例如https://www.percona.com/blog/2011/11/29/avoiding-auto-increment-holes-on-innodb-with-insert-ignore/

  2. 另一个想法是有一个额外的表,其中包含对 其中 count 是 pk pk 来自对 的记录数。因此,可以只计算表的一部分并使用预先计算的值。但我更愿意避免任何额外的结构/缓存,因为实现、验证和支持它们需要时间

【问题讨论】:

    标签: mysql performance count


    【解决方案1】:

    最后我决定计数的精度并不重要。所以有几种解决方案:

    1. TABLE STATUS - 不好,因为 对于 InnoDB,这个值是一个近似值,可能与实际值相差 40% 到 50%
    2. 保持缓存的count 值和pk 为其计算了计数。插入新记录后,计算插入记录的旧 pk (pk_old) 和 pk (pk_new) 之间的记录。任何一种这样的实现都存在问题:由于回滚当前事务,或者另一个事务在 pk_old 和 pk_new 之间插入了一条记录,但尚未提交,计数和实际计数之间的差异可以无限增加
    3. 每次插入后增加缓存count。在服务器重新启动之前,事务可能会回滚并且计数会出错

    我选择了方法#3,因为它既简单又最快(服务器启动时只有一个 sql 查询)

    【讨论】:

      【解决方案2】:

      您预计有多少行?多久需要一次COUNT?这个数字需要有多精确?

      • 如果不需要准确的答案,请参阅TABLE STATUS 中的Rows
      • InnoDB 将使用“最小”索引进行计数,因此添加这样一个索引。 “最小”二级索引可能是最小列(PK 中的第一列除外)上的索引。 TINYINT,如果你有的话,只有 1 个字节。
      • TRIGGER 可以进行计数。
      • 如果稍微陈旧的计数足够好,那么存储在某处的定期COUNT(*) 就可以了。
      • 或者您可以将其视为数据仓库应用程序中“汇总表”的一个简单特例。这可能有每日计数;那么您需要计算今天的行数才能完成总数。 (看到SHOW CREATE TABLE 会有所帮助。然后我可以更具体。)

      【讨论】:

      • 您的大多数建议都不起作用,因为我需要带有pk &lt;= 1072370 等条件的摘要。 “最小”索引是什么意思?
      • @rupashka - 我增加了我的答案。
      • 我想避免使用触发器等任何技巧。通常在长期应用中,任何变通方法都会成为问题,因此我正在寻找最简单的解决方案。是的,我想我可以使用过时的计数,TABLE STATUS 可以提供帮助。我将在这里发布我的最终解决方案
      • 刚刚找到关于TABLE STATUS的信息:对于其他存储引擎,例如 InnoDB,这个值是一个近似值,可能与实际值相差 40% 到 50% (dev.mysql.com/doc/refman/8.0/en/show-table-status.html)。听起来很糟糕
      【解决方案3】:

      您可以稍微改进您的第二个选项。您不需要用于 pk->count 匹配的持久表。您将只使用该表中的最新记录。

      您可以将此值保存在内存/服务中并定期更新它们,而不是表。它仍然需要一些额外的编码工作,但您将避免为计算旧记录而进行过多的表扫描。

      请记住,未提交的事务可能会插入其他事务不可见的记录。换句话说,为这个 pk->count 匹配保留的 pk 值应该小于活动事务中的任何 pk。

      【讨论】:

      • 1.我不清楚第二个选项:我建议不要为所有 pk 保留对,但要保留一些差距(或者甚至选择这样的 pk-s 计数与一些预定义的步骤)。所以我不能计算所有行,而是SELECT MAX(PK) FROM CL_PRECALCULATED WHERE PK &lt;= REQUIRED_PKREQUIRED_PK 之间的行。 2. 是的,服务是个好主意,但如果可能的话我想避免它 3. 因为记录是使用自动增量 PK 插入的,所以预先计算的值没有问题
      • 我不认为有任何其他技巧。在大多数情况下,计算表记录数量的想法很糟糕。获取记录的数量通常是相当昂贵的操作,我尽量避免这种情况。如果您需要费率指标 - 考虑计算每项服务的费率并且不要触摸 db 如果您需要一些不精确的估计,您已经在上面提到了选项
      • 我有一个有很多玩家(100k+)的游戏。可以使用 filter 找到玩家,我会显示玩家总数(如果过滤器为空),否则会显示过滤玩家的数量。我想显示这些数字,因为这对玩家了解游戏中有多少玩家非常有用...
      • 如果是这样。您是否计划拥有禁用用户?不活跃?或其他无法搜索的类别。如果是这样,您需要优化对总计的搜索。另外,我认为您不需要精确数量的玩家。有 100 0001 或 100 005 个玩家可供搜索并不重要。
      • 所有用户都将落入结果中。这是一个关于不精确计数的好通知,谢谢!
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-02-09
      • 2020-07-11
      • 1970-01-01
      • 1970-01-01
      • 2015-12-16
      相关资源
      最近更新 更多