MySQL - 加速查询避免文件排序和临时

Posted

技术标签:

【中文标题】MySQL - 加速查询避免文件排序和临时【英文标题】:MySQL - speed up query avoid filesort and temporary 【发布时间】:2015-08-31 15:39:10 【问题描述】:

我的 mysql 查询很慢。我有 3 个表:工作(20 万条记录)、位置(30 万条)、职位(70 万条)。

SELECT
    j.job_offerid
FROM `job_offer` AS j 
INNER JOIN `job_offer_localitymap` AS d ON d.`job_offerid` = j.`job_offerid` AND 
    `gps_localityid` IN(35, 3301, 3302, 3303, 3305, 3306, 3307, 3308, 124, 3811, 3805, 3709, 3808, 3809) 
WHERE 
    j.`status` = 1 AND 
    j.`job_offerid` IN(
        SELECT `job_offerid` 
        FROM `job_offer_positionmap` 
        WHERE `cb_job_positionid` IN (1001, 6, 629, 7, 8, 9, 10, 11, 12, 13, 1, 15, 16, 17))
ORDER BY j.`job_offerid` DESC 
LIMIT 3

我必须过滤位置和地点,所以我使用了 IN。

解释:使用 where;使用索引;使用临时的;使用文件排序;暂时开始

仅包含已使用行的表格方案:

CREATE TABLE `job_offer` (
  `job_offerid` int(13) NOT NULL AUTO_INCREMENT,
  `status` int(13) NOT NULL DEFAULT '1',
  PRIMARY KEY (`job_offerid`),
  KEY `status` (`status`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

CREATE TABLE `job_offer_localitymap` (
  `job_offer_localitymapid` int(13) NOT NULL AUTO_INCREMENT,
  `gps_localityid` int(13) NOT NULL,
  `job_offerid` int(13) NOT NULL,
  PRIMARY KEY (`job_offer_localitymapid`),
  KEY `gps_localityid` (`gps_localityid`),
  KEY `job_offerid` (`job_offerid`),
  KEY `gps_localityid_job_offerid` (`gps_localityid`,`job_offerid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_czech_ci;

CREATE TABLE `job_offer_positionmap` (
  `job_offer_positionmapid` int(13) NOT NULL AUTO_INCREMENT,
  `cb_job_positionid` int(13) NOT NULL,
  `job_offerid` int(13) NOT NULL,
  PRIMARY KEY (`job_offer_positionmapid`),
  KEY `cb_job_positionid` (`cb_job_positionid`),
  KEY `job_offerid` (`job_offerid`),
  KEY `cb_job_positionid_job_offerid` (`cb_job_positionid`,`job_offerid`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_czech_ci;

索引无处不在。

感谢您的建议

【问题讨论】:

感谢更新。我们来看看:> 抱歉,已添加。这些表是以前的人创建的,很遗憾有混合排序规则和表引擎。 【参考方案1】:

您的加入将受益于复合

job_offer_localitymap.(job_offerid,gps_localityid)

也就是说,与您在该表中的当前组合相反。

因此你可以放弃这两个:

  KEY `gps_localityid` (`gps_localityid`),
  KEY `job_offerid` (`job_offerid`),

因为你会留下两个复合索引,每个最左边的每个都被其他查询使用,这些查询受益于上述两个我刚刚说过要删除


在您的第 5 行查询中,保持一致并使用别名 j,因为我不得不寻找(不长)来查看哪个表


在我看来,job_offer 中的 KEY status (status) 可能相对没用,但我不知道您还有其他查询。但是由于您的数据类型很薄,因此 job_offer(job_offerid,status) 上的复合可能会使您的许多查询飞起来,因为它是 covering index 不需要在数据页之后进行。


至于job_offer_positionmap,这可能是一个连接删除了一个缓慢的子查询和开发人员选择在那里添加一个组合。连接在概念上类似于第一个连接。


我认为 in 子句一般没有问题,因为 mysql CBO 基于成本的优化器应该处理这个问题。


但这些只是建议,因为添加索引并非完全没有缺点。这是一个脆弱的平衡行为,但最终您可能会发现不仅此查询有效,您的其他查询也有效。


【讨论】:

问题是,MySQL首先使用这些索引,而不是复合索引(最初也没有复合索引)。这是一个非常复杂的项目,所以我改用 FORCE INDEX。谢谢你的解决方案!【参考方案2】:

您正在使用join 进行过滤。所以我会将该逻辑移至where 子句:

SELECT j.job_offerid
FROM `job_offer` 
WHERE j.`status` = 1 AND 
      j.`job_offerid` IN (SELECT jop.`job_offerid` 
                          FROM `job_offer_positionmap` jop
                          WHERE `cb_job_positionid` IN (1001, 6, 629, 7, 8, 9, 10, 11, 12, 13, 1, 15, 16, 17)
                         ) AND
      j.`job_offerid` IN (SELECT jop.`job_offerid` 
                          FROM `job_offer_localitymap` jol
                          WHERE jol.gps_localityid IN (35, 3301, 3302, 3303, 3305, 3306, 3307, 3308, 124, 3811, 3805, 3709, 3808, 3809)
                         )                        
ORDER BY j.`job_offerid` DESC 
LIMIT 3;

然后,对于这个查询,您需要以下索引:

job_offer(status, job_offerid desc) job_offer_positionmap(cb_job_positionid, job_offerid) job_offer_localitymap(gps_localityid, job_offerid)

生成的查询应该使用第一个索引进行过滤和order by 子句。然后它将使用其他两个索引进行过滤。

【讨论】:

以上是关于MySQL - 加速查询避免文件排序和临时的主要内容,如果未能解决你的问题,请参考以下文章

如何加速Oracle大批量数据处理?

深入理解MySQL索引原理和实现——为什么索引可以加速查询?

使用 2GB+ 加速单个表上的 SQL 查询

MySQL索引介绍

如何用一款小工具大大加速MySQL SQL语句优化

加速使用 Group By 和 Order By 的多表 Mysql 查询