【问题标题】:MySQL Innodb fail to use index due to extremely wrong rows estimationMySQL Innodb 因行估计错误而无法使用索引
【发布时间】:2017-03-24 02:39:15
【问题描述】:

我有一个 innodb 表,表上的查询如下所示。

SELECT *
FROM x
WHERE now() BETWEEN a AND b

我在 (a,b) 上创建了一个复合索引,查询返回大约 4k 行,而表中的总行数大约是 700k。

但是,当我得到执行计划的EXPLAIN 时,我发现查询没有使用预期的索引。因为估计rows在360k左右,比实际值大很多。

我知道就像许多帖子(例如Why the rows returns by "explain" is not equal to count()?)所解释的那样,EXPLAIN 只能得到粗略的估计。但是FORCE INDEX的解决方案非常棘手,未来可能会带来潜在的性能风险。

有什么方法可以让 MySQL 得到更准确的估计(目前的估计是 90 倍)?谢谢。

【问题讨论】:

  • 不,使用索引失败,因为索引没用。扫描表效率更高。

标签: mysql sql innodb explain sql-execution-plan


【解决方案1】:

这种特殊的结构很难优化:

WHERE constant BETWEEN col1 AND col2

无法设计任何 MySQL 索引来使其快速运行。尝试包括:

INDEX(col1) -- will scan last half of table
INDEX(col2) -- will scan first half of table
INDEX(col1, col2) -- will scan last half of table

(是否在索引BTree中做更多的工作取决于ICP,覆盖等。但是,无论如何,必须触及很多行。)

无法改进的一个原因是“一半”中的“最后”行可能实际上匹配。

如果 (col1, col2) 对不重叠,则有可能通过在一行后停止来提高性能。但是MySQL不知道你有没有这种情况,所以无法优化。 Here 是一种有效的非重叠 IP 地址查找方法。

【讨论】:

    【解决方案2】:

    InnoDB 只保留表的近似行数。这在SHOW TABLE STATUS的文档中有解释:

    行数。一些存储引擎,例如 MyISAM,存储确切的计数。对于其他存储引擎,例如 InnoDB,这个值是一个近似值,可能与实际值相差 40% 到 50%。

    我认为没有任何方法可以让 InnoDB 保持准确的行数,只是它不是这样工作的。

    【讨论】:

    • 感谢您的回复。所以如果我们不把表拆分成小表,那么加快查询的唯一方法就是FORCE INDEX?我担心的是,将来当数据分布发生变化或索引发生变化时,我们可能不会意识到FORCE INDEX 的影响。手动选择索引太麻烦了。
    • 我没有真正研究过,所以无法回答。
    • 我的猜测是他们确定对查询优化的影响还不足以要求他们解决保持准确计数的问题。
    猜你喜欢
    • 2017-06-11
    • 2022-01-06
    • 2014-01-11
    • 2015-05-12
    • 1970-01-01
    • 2022-11-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多