【问题标题】:MySQL performance, inner join, how to avoid Using temporary and filesortMySQL 性能、内连接、如何避免使用临时和文件排序
【发布时间】:2010-07-26 13:25:23
【问题描述】:

我有一个表 1 和表 2。

表 1 PARTNUM - ID_BRAND partnum 是主键 id_brand 已“编入索引”

表 2 ID_BRAND - BRAND_NAME id_brand 是主键 brand_name 已“编入索引”

表 1 包含 100 万条记录,表 2 包含 1.000 条记录。

我正在尝试使用 EXPLAIN 优化一些查询,但经过多次尝试后,我走到了死胡同。

EXPLAIN 
SELECT pm.partnum, pb.brand_name
FROM products_main AS pm 
LEFT JOIN products_brands AS pb ON pm.id_brand=pb.id_brand
ORDER BY pb.brand ASC 
LIMIT 0, 10

查询返回这个执行计划:

ID, SELECT_TYPE, TABLE, TYPE, POSSIBLE_KEYS, KEY, KEY_LEN , REF, ROWS, EXTRA
1, SIMPLE, pm, range, PRIMARY, PRIMARY, 1, , 1000000, Using where; Using temporary; Using filesort
1, SIMPLE, pb, ref, PRIMARY, PRIMARY, 4, demo.pm.id_pbrand, 1,

MySQL 查询优化器在执行计划中显示临时 + 文件排序。 我怎样才能避免这种情况?

“邪恶”在 ORDER BY pb.brand ASC 中。按该外部字段排序似乎是瓶颈..

【问题讨论】:

  • 这些表中有哪些索引?
  • 猜测一下,除非您在 pb.brand 上有索引,否则 mysql 将需要在应用限制之前对所有 1M 行进行排序
  • 表1:PARTNUM是PK,ID_BRAND是加速索引表2:ID_BRAND是PK,BRAND是加速索引

标签: mysql performance join inner-join


【解决方案1】:

首先,我质疑外连接的使用,因为 order by 是在 rhs 上操作的,而左连接注入的 NULL 可能会对它造成严重破坏。

无论如何,加快查询速度的最简单方法是在 pb.id_brand 和 pb.brand 上建立一个覆盖索引。这将允许使用连接条件“使用索引”评估 order by。另一种方法是找到一些方法来减小传递给 order-by 的中间结果的大小。

不过,外连接、order-by 和 limit 的组合让我想知道您到底要查询什么,以及是否可能没有更好的方式来表达查询本身。

【讨论】:

    【解决方案2】:

    尝试用子查询替换连接。 MySQL 的优化器很烂;子查询通常比联接提供更好的性能。

    【讨论】:

    • 你有这个的来源吗...?
    • 我用类似的查询进行了测试,他是对的,它产生了巨大的差异。将 ORDER BY x DESC LIMIT 20 移动到子查询中删除了 Using 临时,查询时间从超过 5 秒变为 0.0017 秒。
    • @ColinM 如何将ORDER BY x DESC LIMIT 20 移动到子查询中?也许您可以发布一个查询示例作为此问题的附加答案?
    • 你不能说,子查询更好。我发现了相反的情况:他们更糟。所以我想看看你论文的一些证据......
    【解决方案3】:

    首先,尝试更改 products_brands 表中的索引。删除brand_name上已有的,新建一个:

    ALTER TABLE products_brands ADD INDEX newIdx (brand_name, id_brand)
    

    然后,该表将已经有一个“orderedByBrandName”索引,其中包含您连接所需的 id,您可以尝试:

    EXPLAIN
    SELECT pb.brand_name, pm.partnum
    FROM products_brands AS pb 
      LEFT JOIN products_main AS pm ON pb.id_brand = pm.id_brand
    LIMIT 0, 10
    

    请注意,我还更改了查询中表的顺序,因此您从 small 开始。

    【讨论】:

    • 我在 PRODUCTS_BRANDS 上有两个索引。第一个在 id_brand 上(它是主键,它有一个隐式索引)。品牌名称上的第二个。
    • 我更改了 JOIN TABLES 的顺序,但没有任何变化。查询优化器选择表的顺序,它不遵循 SQL sintax 的顺序。
    • 您是否更改了索引?我在您的问题中读到您有两个索引:您需要更改brand_name 上的一个,并将其设置为 (brand_name, id_brand)。
    • 我已经添加了一个新索引,正如你所写的。优化器使用新索引(包含 2 列的 1 个索引)。我在输出中看到现在,它已被使用。但是'使用临时;使用 filesort' 并没有消失。执行时间保持不变。
    【解决方案4】:

    这个问题有点过时了,但我确实找到了,其他人也是。

    如果 ORDER BY 或 GROUP BY 包含来自连接队列中第一个表以外的表的列,Mysql 使用临时。

    所以你只需要通过使用 STRAIGHT_JOIN 来反转连接顺序,以绕过优化器发明的顺序:

    SELECT STRAIGHT_JOIN pm.partnum, pb.brand_name
    FROM products_brands AS pb 
    RIGHT JOIN products_main AS pm ON pm.id_brand=pb.id_brand
    ORDER BY pb.brand ASC 
    LIMIT 0, 10
    

    还要确保将 max_heap_table_size 和 tmp_table_size 变量设置为足够大的数字以存储结果:

    SET global tmp_table_size=100000000;
    SET global max_heap_table_size=100000000;
    

    -- 在本例中为 100 兆字节。这些也可以在 my.cnf 配置文件中设置。

    【讨论】:

      猜你喜欢
      • 2023-03-30
      • 1970-01-01
      • 2021-11-11
      • 2010-11-08
      • 2010-11-29
      • 2019-11-20
      • 1970-01-01
      • 2016-09-04
      • 2010-10-16
      相关资源
      最近更新 更多