具有多个连接的 MySQL 查询执行时间过长

Posted

技术标签:

【中文标题】具有多个连接的 MySQL 查询执行时间过长【英文标题】:MySQL query with multiple joins taking too long to execute 【发布时间】:2017-04-26 15:49:04 【问题描述】:

我有 3 张桌子。第一个叫map_life,第二个叫scripts,第三个叫npc_data

我正在运行以下查询以从map_life 获取所有属性,同时还从scripts 获取script 列,如果ID 匹配,则从npc_data 获取storage_cost 列。

SELECT life.*
     , script.script
     , npc.storage_cost 
  FROM map_life life 
  LEFT 
  JOIN scripts script 
    ON script.objectid = life.lifeid 
   AND script.script_type = 'npc' 
  LEFT 
  JOIN npc_data npc 
    ON npc.npcid = life.lifeid

如您所见,map_life id 是lifeid,而scripts id 是objectidnpc_data id 是npcid

这个查询大约需要 5 秒来执行,我不知道为什么。这是所有这 3 个表的 CREATE 语句,也许我遗漏了什么?

CREATE TABLE  `mcdb83`.`map_life` (
  `id` bigint(21) unsigned NOT NULL AUTO_INCREMENT,
  `mapid` int(11) NOT NULL,
  `life_type` enum('npc','mob','reactor') NOT NULL,
  `lifeid` int(11) NOT NULL,
  `life_name` varchar(50) DEFAULT NULL COMMENT 'For reactors, specifies a handle so scripts may interact with them; for NPC/mob, this field is useless',
  `x_pos` smallint(6) NOT NULL DEFAULT '0',
  `y_pos` smallint(6) NOT NULL DEFAULT '0',
  `foothold` smallint(6) NOT NULL DEFAULT '0',
  `min_click_pos` smallint(6) NOT NULL DEFAULT '0',
  `max_click_pos` smallint(6) NOT NULL DEFAULT '0',
  `respawn_time` int(11) NOT NULL DEFAULT '0',
  `flags` set('faces_left') NOT NULL DEFAULT '',
  PRIMARY KEY (`id`),
  KEY `lifetype` (`mapid`,`life_type`)
) ENGINE=InnoDB AUTO_INCREMENT=32122 DEFAULT CHARSET=latin1;

CREATE TABLE  `mcdb83`.`scripts` (
  `script_type` enum('npc','reactor','quest','item','map_enter','map_first_enter') NOT NULL,
  `helper` tinyint(3) NOT NULL DEFAULT '-1' COMMENT 'Represents the quest state for quests, and the index of the script for NPCs (NPCs may have multiple scripts).',
  `objectid` int(11) NOT NULL DEFAULT '0',
  `script` varchar(30) NOT NULL DEFAULT '',
  PRIMARY KEY (`script_type`,`helper`,`objectid`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COMMENT='Lists all the scripts that belong to NPCs/reactors/etc. ';

CREATE TABLE  `mcdb83`.`npc_data` (
  `npcid` int(11) NOT NULL,
  `storage_cost` int(11) NOT NULL DEFAULT '0',
  `flags` set('maple_tv','is_guild_rank') NOT NULL DEFAULT '',
  PRIMARY KEY (`npcid`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

【问题讨论】:

好。接下来为上述内容提供解释。 【参考方案1】:

对于这个查询:

SELECT l.*, s.script, npc.storage_cost 
FROM map_life l LEFT JOIN
     scripts s 
     ON s.objectid = l.lifeid AND
        s.script_type = 'npc' LEFT JOIN
     npc_data npc 
     ON npc.npcid = l.lifeid;

您希望索引:scripts(object_id, script_type, script)npc_data(npcid, storage_cost)。这些索引中列的顺序很重要。

【讨论】:

【参考方案2】:

    map_life.lifeid 没有定义任何索引,因此连接将导致全表扫描。在map_life.lifeid 字段上定义索引。

    在脚本表中,主键按顺序在以下字段中定义:script_typehelperobjectid。连接在objectid 上完成,script_type 上有一个常量过滤条件。由于索引中的字段顺序错误,mysql 无法使用主键进行本次查询。对于此查询,索引中字段的顺序应为:objectidscript_typehelper

以上将显着加快连接速度。如果您的索引实际上涵盖了查询中的所有字段,您可能会进一步提高查询速度,因为在这种情况下 MySQL 甚至不必接触表。

考虑向scripts 表添加具有以下字段和顺序的索引:object_id, script_type, scriptnpcid, storage_cost 索引到npc_data 表。但是,这些索引可能会减慢插入/更新/删除语句的速度,因此在将这些索引添加到生产环境之前,请进行一些性能测试。

【讨论】:

以上是关于具有多个连接的 MySQL 查询执行时间过长的主要内容,如果未能解决你的问题,请参考以下文章

AWS RDS MySql 具有复合索引和日期范围的简单查询在大约 800 万个数据中执行时间过长

具有多个连接的 MySQL 查询的低效执行计划

MySQL innoDB:查询执行时间过长

在 oracle 中,具有多个连接的查询执行需要更多时间

优化多个 MySQL 视图的连接

为啥在具有多个连接的 WHERE 子句中,子查询比文字值执行得更好?