为什么InnoDB不存储行数?


19

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

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

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

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


6
慢点 不精确,不。这很慢,因为它可以给出准确的结果。当您有一个200M的行表,并且可能有许多其他事务每秒插入/删除同一张表时,另一个问题是“您需要确切的数字吗?”
ypercubeᵀᴹ

@ypercube我知道我在phpmyadmin中看到了几次非常不正确的行计数值。另外,这里有一条评论说“可能不准确”。
Radu Murzea 2012年

1
@RaduMurzea phpMyAdmin用户可以使用另一种方法来计算InnoDB表的表计数,其原因是您知道的速度原因。这就是您提到的不准确之处。实际SELECT COUNT(*) FROM ...查询是精确的。如果愿意,可以将phpMyAdmin配置为始终使用精确的行数,但会牺牲速度。更多信息:stackoverflow.com/questions/11926259/…–
DOOManiac

Answers:


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以一种好方法,一种坏方法或根本不影响性能。试试看吧!


16

对于初学者来说,没有“当前计数”这样的东西要存储在变量中。像这样的查询SELECT COUNT(*) FROM ...受当前隔离级别和所有并发暂挂事务的约束。根据隔离级别,查询可以查看是否看到未决未提交事务插入或删除的行。回答的唯一方法是对当前事务可见的行进行计数。

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


1
好的,所以这取决于隔离级别,这才有意义。但是它仍然可以实现。
Radu Murzea 2012年

@SoboLAN不应这样做的原因有很多,上面列出了大多数原因。您是否将通过维护每个事务开始时每张表的计数列表来实现它(无论Oracle的SCN在MySQL中是什么)?管理这样的计数将是巨大的开销-考虑一个具有100或1000个并发会话的数据库,每个会话在同一张表上执行大量INSERT / DELETE。不可能维护。
菲尔(Philᵀᴹ)2012年

实现这一点非常困难。只是认为该计数必须保留在数据库中,这意味着在元数据中的某个位置,并且此计数必须由插入或删除行的每个事务来维护。您如何锁定该元数据?您将如何处理回滚?并非无关紧要。结果将可用于非常狭窄的查询子集。
Remus Rusanu

3
@JackDouglas有趣。从我过去所看到的情况来看,COUNT(*)现实中很少需要查询,并且通常是开发人员缺乏经验(在选择行之前先对行进行计数!)或错误的应用程序设计的结果。
菲尔(Philᵀᴹ)2012年

1
@SoboLAN-不,不是。拥有在预定时间间隔更新某种统计信息表的服务会更好。想象一下,如果有一个大型数据库,并且有几个管理员使用来查询大多数表,则向表中SELECT COUNT(*)添加未优化WHERE的表,您将有几个用户将数据库屈服于几个可疑有用的统计计数器。
NB 2012年

0

从理论上讲,使用InnoDB可以精确地计算给定表的行数,但这将以大量锁定为代价,这会对性能产生负面影响。根据隔离级别,它也会有所不同。

MyISAM已经进行了表级锁定,因此没有额外的费用。

尽管很少使用COUNT(*),但我很少需要为表计算行数。我通常附有WHERE子句。在较小的结果集上使用有效索引,我发现它们足够快。

我不同意计数不正确。计数代表数据的快照,我一直发现它们是准确的。

简而言之,MySQL由您自己来为InnoDB实现。您可以存储一个计数,并在每次查询后递增/递减。但是,更简单的解决方案可能是切换到MyISAM。


2
这是不是可以保持的行的准确计数的事务处理系统。因为行数和活动事务数一样多(正确)。
a_horse_with_no_name 2012年

5
我在这里给-1表示“尽管,更简单的解决方案可能是切换到MyISAM。” 我绝不建议仅仅为了获取行数而切换到MyISAM。
德里克·唐尼

@a_horse_with_no_name,因此您同意每个事务的行计数都正确。对我来说似乎是可能的。
马库斯·亚当斯

1
@DTest,我从没说过“仅仅获得行数”。
马库斯·亚当斯

@a_horse_with_no_name,这似乎不正确。当然,仅在事务提交正确时我们才在计算行数吗?
Pacerier,2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.