MySql 执行路径突然变化很大,不一致且缓慢

MySql execution path suddenly varies a lot, is inconsistent and slow

我在 MySQL 的执行路径上遇到问题,导致查询缓慢且不一致。这是一个全新的现象。我们有其他 tables 具有相同的设置(好吧,尽可能接近),这很好,但出于某种原因,创建新的 tables 现在有这个 slow/inconsistent 问题。

我们使用的版本: "mysql Ver 14.14 Distrib 5.6.31, for debian-linux-gnu" 与 InnoDB。数据库存在于一个 vagrant box 中。

该行为在另一台计算机上重现,并且是在全新版本的 vagrant box 之后。

正如我所说,数据库在我本地机器上的一个 vagrant box 中,我的机器负载不重。

t1 大约有 100 万行。 t2 是一个新的 table.

这是始终重现问题的最简单的查询:

SELECT
    *
FROM
    redacted_t1 AS t1
        JOIN
    redacted_t2 AS t2 ON t1.a_column = t2.id
WHERE
t2.c_column != 'asdff'
ORDER BY t1.b_column DESC;

请参阅下面一些执行路径缓慢(超过 3 秒)的示例

我已经看到至少 2 个其他执行路径(它们也很慢)但是因为很难重现(随机?)我不能在这里 post 它们。

有时,但不经常,我不知道如何或为什么会发生以下执行路径:

这非常快,0.00 秒。有时拥有一个全新版本的数据库(如在一个新的 vagrant box 中),并且 运行ning 在 t1 和 t2 上进行优化会产生这个结果。有时优化 什么也没做。有时这种执行状态是在没有优化table的情况下实现的。请注意,与慢速执行路径相比,t1 的 'rows' 计数要低得多。 这与我 运行 "SHOW STATUS;".

看到的一致
CREATE TABLE `redacted_t2` (
  `id` int(11) NOT NULL AUTO_INCREMENT,

  -- redacted

  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8 COLLATE=utf8_swedish_ci

CREATE TABLE `redacted_t1` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `a_column` int(11) DEFAULT NULL,

  -- redacted

  PRIMARY KEY (`id`),

  -- redacted

  KEY `redacted_t1_a_column` (`a_column`),

  -- redacted

  CONSTRAINT `fk_redacted_t1_2032420404` FOREIGN KEY (`a_column`) REFERENCES `redacted_t2` (`id`),
) ENGINE=InnoDB AUTO_INCREMENT=redacted DEFAULT CHARSET=utf8 COLLATE=utf8_swedish_ci

所以我有几个问题:

1)为什么执行路径如此不一致,为什么我们以前从来没有遇到过?

2) 我们如何解决这个问题,使本应花费 0.00 秒的查询不会随机花费 3 秒?

您可以尝试 运行 EXPLAIN EXTENDED,然后 SHOW WARNINGS,以获得有关查询执行计划的更多详细信息。有关详细信息,请参阅 8.8.3 Extended EXPLAIN Output Format

您还可以在 t1t2 上尝试 运行 ANALYZE TABLE 以确保 MySQL 在选择其时使用更新的 table 统计信息执行计划。

redacted_t2.c_column 上添加索引可能会有所帮助,因为您正在过滤该列。

EXPLAIN 输出来看,似乎 MySQL 有时没有使用索引 redacted_t1_a_column。您可以鼓励或强制数据库使用带有 index hints 的索引,例如USE INDEXFORCE INDEX.

SET innodb_stats_sample_pages = 30;
ANALYZE TABLE t1;
ANALYZE TABLE t2;

然后看看是不是比较一致。由于您 运行 >5.6.6,统计数据应该是 'persistent'。不要使用 OPTIMIZE TABLE.

继续优化:

您真的需要两个 table (SELECT *) 中的所有列吗?它在优化和索引方面有所不同。您是否向我们展示了所有相关指标?是否有 TEXTBLOB 列?您需要取回它们吗?

table 的百分比是 t2.c_column != 'asdff'?如果它是一个小百分比,那么你需要 INDEX(c_column).

t2只有5行吗?如果是这样,索引、解释计划等将无关紧要。

已解决。

显然,优化器...不是很好。如果优化器无法处理您的查询,请使查询稍微复杂一些。

所以我在 ORDER BY 中添加了一列。这解决了一切。不理想,但出于某种原因它有效。

SELECT
    *
FROM
    redacted_t1 AS t1
        JOIN
    redacted_t2 AS t2 ON t1.a_column = t2.id
WHERE
t2.c_column != 'asdff'
ORDER BY t1.b_column, t2.id DESC;