为什么在按关键字排序之前执行 select-statement 中的 MariaDB 所有子查询,即使它们不是必需的?

Why executes MariaDB all subqueries in select-statement before order by keyword, even if they are not necessary?

一周前我们将数据库从 mySQL8 切换到 MariaDB10,现在我们遇到了巨大的性能问题。我们找到了原因:我们经常在 select 语句和 ORDER BY 中使用子查询。这是一个例子:

SELECT id, (SELECT id2 FROM table2 INNER JOIN [...] WHERE column.foreignkey = table.id) queryResult
FROM table
WHERE status = 5
ORDER BY column
LIMIT 10

想象一下,table 中有 1.000.000 个条目受到影响,如果 status = 5

在 mySQL8: ORDER BYLIMIT 中发生了什么,然后执行子查询(影响 10 行)

MariaDB10 中发生了什么: 子查询执行(影响 1.000.000 行),然后是 ORDER BYLIMIT

两个查询都返回 10 行,但在 MariaDB10 下,它的速度非常慢。为什么会这样? MariaDB 中是否有一个我们应该激活的选项来避免这种情况?我从 mySQL8 得知 select 子查询将在 ORDER BY 中被提及时执行。但如果没有,它们将在结果集存在时执行。

信息:如果我们这样做,一切都很好:

SELECT *, (SELECT id2 FROM table2 INNER JOIN [...] WHERE column.foreignkey = outerTable.id) 
FROM (
    SELECT id
    FROM table
    WHERE status = 5
    ORDER BY column
    LIMIT 10
) outerTable

非常感谢您的帮助。

是的,必须在 order by 之前 为每一行评估子查询。子查询似乎只需要id,所以你可以这样表述:

SELECT id,
       (SELECT id2 FROM table2 INNER JOIN [...] WHERE column.foreignkey = t.id) as queryResult
FROM (SELECT t.*
      FROM table t
      WHERE status = 5
      ORDER BY column
      LIMIT 10
     ) t

仅在从 table 中选择行后才计算子查询。

这是因为 table 一堆未排序的行

A "table" (and subquery in the FROM clause too) is - according to the SQL standard - an unordered set of rows. Rows in a table (or in a subquery in the FROM clause) do not come in any specific order. That's why the optimizer can ignore the ORDER BY clause that you have specified. In fact, the SQL standard does not even allow the ORDER BY clause to appear in this subquery (we allow it, because ORDER BY ... LIMIT ... changes the result, the set of rows, not only their order).

mariadb manual

所以优化器删除并忽略了 ORDER BY。

您已经找到了在子查询中使用 LIMIT 和 ORDER By 来规避它的方法

在 5.6 中,MariaDB 和 MySQL 针对优化器朝着不同的方向发展。 MariaDB 非常关注子查询,尽管可能会损害此特定查询。

你有INDEX(status, column)吗?这将有助于此查询的大多数变体。

在搜索和搜索之后,我终于找到了一个解决方案,可以使 mariaDB10 数据库按照我从 mySQL8 了解到的方式工作。

对于那些有类似问题的人:每次连接到服务器时设置它,一切都像 mySQL8:

SET optimizer_use_condition_selectivity = 1

长版:我上面描述的问题一下子解决了,子查询像以前在mySQL8下一样执行了。我什么也没做!

但很快又出现了新的问题:我们有一个统计页面,速度非常慢。我注意到缺少一个索引,我添加了它。我执行了查询并且它正在运行。在添加 38 之后,没有索引 100.000 行影响查找结果。干得好。

然后奇怪的事情开始发生了:我再次执行查询,数据库没有使用索引。所以我一次又一次地执行它。这是结果:

第一次查询执行(我用 ANALYZE 执行):100.000 行受影响

第二次查询执行:38 行受影响

第 3 次查询执行:38 行受影响

第 4 次查询执行:100.000 行受影响

第 5 次查询执行:100.000 行受影响

这是完全随机的,即使在我们的 SaaS 解决方案中也是如此!所以我开始研究优化器如何确定执行计划。我发现了这个:optimizer_use_condition_selectivity

mariaDB10.4 服务器的默认值为 4,这意味着直方图用于计算结果集。我看了一些关于它的视频,并意识到这在我们的案例中不起作用(尽管我们坚持使用数据库规范化)。模式 1 效果很好:

use selectivity of index backed range conditions to calculate the cardinality of a partial join if the last joined table is accessed by full table scan or an index scan

我希望这能帮助其他像我一样对此感到绝望的人。