为什么 InnoDB 不存储行数?

Rad*_*zea 21 mysql innodb count

每个人都知道,在使用 InnoDB 作为引擎的表中,诸如此类SELECT COUNT(*) FROM mytable的查询非常不准确且非常慢,尤其是当表变大并且在该查询执行时不断插入/删除行时。

据我了解,InnoDB 不会将行数存储在内部变量中,这就是导致此问题的原因。

我的问题是:为什么会这样?存储这样的信息有那么难吗?在许多情况下,这是一个重要的信息。我看到是否要实现这样的内部计数的唯一困难是当涉及事务时:如果事务未提交,您是否计算它插入的行数?

PS:我不是数据库方面的专家,我只是一个将 MySQL 作为一个简单爱好的人。因此,如果我只是问一些愚蠢的问题,请不要过分挑剔:D。

Rem*_*anu 16

对于初学者来说,没有诸如“当前计数”之类的东西要存储在变量中。类似查询SELECT COUNT(*) FROM ...受当前隔离级别和所有并发挂起事务的约束。根据隔离级别,查询可以看到或看不到由挂起的未提交事务插入或删除的行。唯一的答案是计算当前事务可见的行数。

请注意,我什至没有触及计数期间开始或结束的并发事务的更棘手的主题。更不用说回滚了...

  • @JackDouglas 有趣。从我过去所见,在现实中很少需要`COUNT(*)` 查询,通常是开发人员缺乏经验(在我们选择行之前计算行数!)或糟糕的应用程序设计的结果。 (3认同)

Rol*_*DBA 9

我同意@RemusRusanu(他的回答+1)

SELECT COUNT(*) FROM mydb.mytableInnoDB 中的行为就像事务存储引擎应该的那样。将其与 MyISAM 进行比较。

我的ISAM

如果mydb.mytable是 MyISAM 表,启动SELECT COUNT(*) FROM mydb.mytable;就像运行SELECT table_rows FROM information_schema.table WHERE table_schema = 'mydb' AND table_name = 'mytable';. 这会触发对 MyISAM 表头中行数的快速查找。

数据库

如果mydb.mytable是 InnoDB 表,你会得到一大堆正在发生的事情。你有 MVCC 正在进行,管理以下内容:

  • ib_logfile0/ib_logfile1(重做日志)
  • ibdata1
    • 撤消日志
    • 回滚
    • 数据字典更改
  • 缓冲池管理
  • 事务隔离(4种)
    • 可重复读取
    • 读已提交
    • 读未提交
    • 可序列化

向 InnoDB 询问表计数需要通过这些不祥的事情进行导航。事实上,人们永远不会真正知道SELECT COUNT(*) from mydb.mytable是只计算可重复读取还是包括已提交的读取和未提交的读取。

您可以尝试通过启用innodb_stats_on_metadata 来稍微稳定一下

根据innodb_stats_on_meta_data上的 MySQL 文档

启用此变量时(这是默认值,与创建变量之前一样),InnoDB 在元数据语句(例如 SHOW TABLE STATUS 或 SHOW INDEX)期间或访问 INFORMATION_SCHEMA 表 TABLES 或 STATISTICS 时更新统计信息。(这些更新类似于 ANALYZE TABLE 发生的情况。)禁用时,InnoDB 在这些操作期间不会更新统计信息。禁用此变量可以提高具有大量表或索引的模式的访问速度。它还可以提高涉及 InnoDB 表的查询的执行计划的稳定性。

在设置 EXPLAIN 计划方面,禁用它可能会也可能不会为您提供更稳定的计数。它可能SELECT COUNT(*) from mydb.mytable以好的方式、坏的方式或根本不影响性能。试试看!