繁体   English   中英

MySQL选择索引的性能会降低类似的查询

[英]MySQL select performance with Indexes degrades for similar queries

我有一个模式

applicants - id, max_res_id, max_visa_id
applicant_files - id, applicatid, fileid, filetype
files - id, name, filetype

申请人-

CREATE TABLE `applicants` (
  `id` char(36) NOT NULL,
  `max_res_id` char(36) NOT NULL,
  `max_visa_id` char(36) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_res_id` (`max_res_id`) USING BTREE,
  KEY `idx_visa_id` (`max_visa_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

applicant_files

CREATE TABLE `applicant_files` (
  `id` char(36) CHARACTER SET latin1 NOT NULL,
  `applicantid` char(36) CHARACTER SET latin1 DEFAULT NULL,
  `fileid` char(36) CHARACTER SET latin1 DEFAULT NULL,
  `filetype` tinyint(1) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `q_applicantfile_fileid` (`fileid`),
  KEY `u_applicantfile_applid` (`applicantid`),
  KEY `idx_filetype` (`filetype`) USING BTREE,
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `files` (
  `id` char(36) NOT NULL,
  `filetype` tinyint(1) NOT NULL,
  `name` text,
  PRIMARY KEY (`id`),
  KEY `idx_filetype` (`filetype`) USING BTREE,
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

max_res_id, max_visa_id referes到的“ID” applicant_files fileid指的是“ID” files

现在我有2个不同的查询-

select  f.id as resumeId, f.name as resumeName, f.date_entered as resumeDate,
        a.id as applId
    from  oepl_applicants a
    inner join  applicant_files af
              ON ( a.id in ('id1', 'id2')
              and  a.id = af.applicantid
              and  a.max_res_id = af.id
              and  af.filetype = 1
              and  a.max_res_id != ''
              and  a.max_res_id is not null )
    inner join  files f
              ON ( af.fileid = f.id
              and  f.filetype = 1 ) 
select  f.id as visaId, f.name as visaName, f.date_entered as visaDate,
        a.id as applId
    from  oepl_applicants a
    inner join  applicant_files af 
              ON ( a.id in ('id1', 'id2')
              and  a.id = af.applicantid
              and  a.max_visa_id = af.id
              and  af.filetype = 2
              and  a.max_visa_id != ''
              and  a.max_visa_id is not null )
    inner join  files f 
              ON ( af.fileid = f.id
              and  f.filetype = 2 )

对于200个ID(ID1,ID2,... ID200),第一个查询在2秒内返回结果,而第二个查询在30秒内返回结果。

这里可能出什么问题了?

这两个查询的唯一区别是文件类型不同,而联接位于2个不同的列上。 PS-与max_res_id中的值相比,max_visa_id中的许多值都为空(空)

谢谢您的CREATE TABLEs

latin1 vs utf8上加入会使索引的使用无效!

尽管在这种情况下无关紧要,但是请将“ filtering”子句移至WHERE子句,而仅在ON子句中保留描述表之间关系的子句。 例如,在第一个查询中:

    inner join  applicant_files af
              ON   a.id = af.applicantid
              and  a.max_res_id = af.id
    inner join  files f  ON  af.fileid = f.id
            WHERE  a.id in ('id1', 'id2')
              and  f.filetype = 1
              and  af.filetype = 1
              and  a.max_res_id != ''
              and  a.max_res_id is not null

优化程序将决定查看表的顺序。 从“过滤”子句中,我们希望看到以下内容:

a:        INDEX(max_res_id, id)
af and f: INDEX(filetype)  -- but see note below

然后,优化程序将查看进入“下一个”表是否容易。 这些可能是有益的。 (我注意到您已经有了(id) 。)

af:  INDEX(applicantid, filetype)  -- in either order

请运行EXPLAIN SELECT来查看优化器选择的顺序,以及选择的索引以获取每个后续​​表。

char(36)闻起来像UUID或GUID。 最好将它们CHARACTER SET latin1CHARACTER SET latin1而不是utf8 但是由于随机性,这样的字段对于索引很糟糕。 我的博客 如果可能,请切换到MEDIUMINT UNSIGNED AUTO_INCREMENT尽管这会涉及很多代码和架构更改。

这两个filetype实例是否冗余? 也就是说,您是否需要检查两个表的文件类型? 那是很多额外的工作。

为了帮助进行初始查询,我们需要确定filetype值的分布。 12多(或少)常见吗? 在表的开头(或结尾)附近是否聚集了1 (或2 )行?

桌子多大? 如果innodb_buffer_pool_size是什么值? 你有多少RAM?

以下某些或全部可能合谋为您带来较差的表现:

  • UUID的随机性。
  • 大于可容纳在buffer_pool中的表。
  • RAM不足,无法使buffer_pool大。
  • buffer_pool太大(相对于RAM),以至于发生交换。

如果这些注释不能提供足够的速度,我建议您重组查询以延迟从f进行获取:

SELECT f..., x...
    FROM (
        SELECT ... FROM applicants AS a
                   JOIN applicant_files AS af ON ...
                 WHERE ...
         ) AS x
    JOIN files AS f  ON x.fileid = f.id
    WHERE f.filetype = 1

警告 :“此处显示的架构是缩小版。”- 由于您的缩小, 我建议的内容可能还不够!

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM