优化 MySQL 3 个表之间的左连接查询以减少执行时间

Optimizing MySQL Left join query between 3 tables to reduce execution time

我有以下查询:

SELECT region.id, region.world_id, min_x, min_y, min_z, max_x, max_y, max_z, version, mint_version 
FROM minecraft_worldguard.region 
LEFT JOIN minecraft_worldguard.region_cuboid 
ON region.id = region_cuboid.region_id 
AND region.world_id = region_cuboid.world_id 
LEFT JOIN minecraft_srvr.lot_version 
ON id=lot 
WHERE region.world_id = 10 
AND region_cuboid.world_id=10;

Mysql 慢速查询日志告诉我执行时间超过 5 秒,return 2300 行但检查了 15'404'545 行 return 它。

这三个表各有大约 6500 行,只有 id 和 lot 字段上的唯一键以及 world_id 字段上的键。我试图通过按 ID 和 world_id 上的双 WHERE 过滤长方体和世界来最大程度地减少检查的行数,但它似乎没有帮助。

知道如何优化这个查询吗?

Here is the sqlfiddle 包含当前状态的索引。

您已在 region.world_id 和 region_cuboid.world_id 上加入了两个表 "minecraft_worldguard.region" 和 "minecraft_worldguard.region_cuboid"。所以 WHERE 子句不需要两个条件。

WHERE 子句中的两列已在 JOIN 条件中等同,因此您不需要检查 WHERE 子句中的两个条件。在 WHERE 子句中删除其中一个,并在保留在 WHERE 条件的列上添加索引。

在您的示例中,将 WHERE 子句保留如下: 其中 region.world_id = 10

并在 region.world_id 列上添加索引,这会稍微提高性能。

注意:请注意,我建议您放弃 WHERE 子句的 "AND region_cuboid.world_id=10;" 部分。

希望对您有所帮助。

MySQL 在这种情况下不能使用索引,因为连接的字段具有不同的数据类型:

  `lot` varchar(20) COLLATE utf8_unicode_ci NOT NULL
  `id` varchar(128) COLLATE utf8_bin NOT NULL

如果您将此字段的类型更改为一般类型(例如,region.idutf8_unicode_ci),MySQL 使用主键(fiddle)。

根据docs

Comparison of dissimilar columns (comparing a string column to a temporal or numeric column, for example) may prevent use of indexes if values cannot be compared directly without conversion.

首先,在编写具有多个 table 的查询时,习惯 "alias" 对 table 的引用是一件非常好的事情,这样您就不会重新键入整个长名称。此外,确定这些列来自哪些 table 是一个非常好的主意,这样可以让用户更好地了解什么是什么,这也有助于提高性能(例如建议覆盖索引)。

就是说,我已经为您的原始查询应用了别名,但我猜测每个列的 table,但您显然可以快速识别并进行调整。

SELECT 
      R.id, 
      R.world_id, 
      RC.min_x, 
      RC.min_y, 
      RC.min_z, 
      RC.max_x, 
      RC.max_y, 
      RC.max_z, 
      LV.version, 
      LV.mint_version 
   FROM 
      minecraft_worldguard.region R
         LEFT JOIN minecraft_worldguard.region_cuboid RC
            ON R.id = RC.region_id 
            AND R.world_id = RC.world_id 
         LEFT JOIN minecraft_srvr.lot_version LV
            ON R.id = LV.lot 
   WHERE 
      R.world_id = 10 

我还从 where 子句中删除了你的 "region_cuboid.world_id = 10",因为基于区域和世界的 JOIN 子句是多余的。

对于索引的建议,如果我有对列的正确别名引用,我会建议在区域 table 上使用覆盖索引 ( world_id, id )。第一个位置的 "World_id" 快速限定 WHERE 子句,"id" 用于 RC 和 LV tables。

对于 region_cuboid table,我还会在 (world_id, region_id) 上有一个索引,以匹配要加入的区域 table它。

对于lot_versiontable,索引在(lot)上或覆盖索引在(lot,version,mint_version)