簡體   English   中英

如何在MySQL中優化此查詢

[英]How to optimize this query in MySQL

我有以下兩個表(Moodle 2.8):

CREATE TABLE `mdl_course` (
  `id` bigint(10) NOT NULL AUTO_INCREMENT,
  `category` bigint(10) NOT NULL DEFAULT '0',
  `sortorder` bigint(10) NOT NULL DEFAULT '0',
  `fullname` varchar(254) NOT NULL DEFAULT '',
  `shortname` varchar(255) NOT NULL DEFAULT '',
  `idnumber` varchar(100) NOT NULL DEFAULT '',
  `summary` longtext,
  `summaryformat` tinyint(2) NOT NULL DEFAULT '0',
  `format` varchar(21) NOT NULL DEFAULT 'topics',
  `showgrades` tinyint(2) NOT NULL DEFAULT '1',
  `newsitems` mediumint(5) NOT NULL DEFAULT '1',
  `startdate` bigint(10) NOT NULL DEFAULT '0',
  `marker` bigint(10) NOT NULL DEFAULT '0',
  `maxbytes` bigint(10) NOT NULL DEFAULT '0',
  `legacyfiles` smallint(4) NOT NULL DEFAULT '0',
  `showreports` smallint(4) NOT NULL DEFAULT '0',
  `visible` tinyint(1) NOT NULL DEFAULT '1',
  `visibleold` tinyint(1) NOT NULL DEFAULT '1',
  `groupmode` smallint(4) NOT NULL DEFAULT '0',
  `groupmodeforce` smallint(4) NOT NULL DEFAULT '0',
  `defaultgroupingid` bigint(10) NOT NULL DEFAULT '0',
  `lang` varchar(30) NOT NULL DEFAULT '',
  `theme` varchar(50) NOT NULL DEFAULT '',
  `timecreated` bigint(10) NOT NULL DEFAULT '0',
  `timemodified` bigint(10) NOT NULL DEFAULT '0',
  `requested` tinyint(1) NOT NULL DEFAULT '0',
  `enablecompletion` tinyint(1) NOT NULL DEFAULT '0',
  `completionnotify` tinyint(1) NOT NULL DEFAULT '0',
  `cacherev` bigint(10) NOT NULL DEFAULT '0',
  `calendartype` varchar(30) NOT NULL DEFAULT '',
  PRIMARY KEY (`id`),
  KEY `mdl_cour_cat_ix` (`category`),
  KEY `mdl_cour_idn_ix` (`idnumber`),
  KEY `mdl_cour_sho_ix` (`shortname`),
  KEY `mdl_cour_sor_ix` (`sortorder`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `mdl_log` (
  `id` bigint(10) NOT NULL AUTO_INCREMENT,
  `time` bigint(10) NOT NULL DEFAULT '0',
  `userid` bigint(10) NOT NULL DEFAULT '0',
  `ip` varchar(45) NOT NULL DEFAULT '',
  `course` bigint(10) NOT NULL DEFAULT '0',
  `module` varchar(20) NOT NULL DEFAULT '',
  `cmid` bigint(10) NOT NULL DEFAULT '0',
  `action` varchar(40) NOT NULL DEFAULT '',
  `url` varchar(100) NOT NULL DEFAULT '',
  `info` varchar(255) NOT NULL DEFAULT '',
  PRIMARY KEY (`id`),
  KEY `mdl_log_coumodact_ix` (`course`,`module`,`action`),
  KEY `mdl_log_tim_ix` (`time`),
  KEY `mdl_log_act_ix` (`action`),
  KEY `mdl_log_usecou_ix` (`userid`,`course`),
  KEY `mdl_log_cmi_ix` (`cmid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

而這個查詢:

SELECT l.id,
       l.userid AS participantid,
       l.course AS courseid,
       l.time,
       l.ip,
       l.action,
       l.info,
       l.module,
       l.url
FROM   mdl_log l
INNER JOIN mdl_course c ON l.course = c.id AND c.category <> 0      
WHERE 
      l.id > [some large id]
      AND
      l.time > [some unix timestamp]
ORDER BY l.id ASC
LIMIT 0,200

mdl_log表具有超過2億條記錄,我需要使用PHP將其導出到文件中,而不能故意死亡。 這里的主要問題是執行速度太慢。 這里的主要殺手是對mdl_course表的聯接。 如果我將其刪除,一切都會很快進行。

這里是解釋:

 +----+-------------+-------+-------+---------------------------------------------+----------------------+---------+----------------+------+-----------------------------------------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+---------------------------------------------+----------------------+---------+----------------+------+-----------------------------------------------------------+ | 1 | SIMPLE | c | range | PRIMARY,mdl_cour_cat_ix | mdl_cour_cat_ix | 8 | NULL | 3152 | Using where; Using index; Using temporary; Using filesort | | 1 | SIMPLE | l | ref | PRIMARY,mdl_log_coumodact_ix,mdl_log_tim_ix | mdl_log_coumodact_ix | 8 | xray2qasb.c.id | 618 | Using index condition; Using where | +----+-------------+-------+-------+---------------------------------------------+----------------------+---------+----------------+------+-----------------------------------------------------------+ 

有什么辦法可以消除對臨時文件和文件排序的使用? 您在這里提出什么?

經過一些測試后,此查詢可以按預期快速運行:

SELECT l.id,
       l.userid AS participantid,
       l.course AS courseid,
       l.time,
       l.ip,
       l.action,
       l.info,
       l.module,
       l.url
FROM   mdl_log l
WHERE 
      l.id > 123456
      AND
      l.time > 1234
      AND
      EXISTS (SELECT * FROM mdl_course c WHERE l.course = c.id AND c.category <> 0  )
ORDER BY l.id ASC
LIMIT 0,200

感謝JamieD77的建議!

執行計划:

 +----+--------------------+-------+--------+-------------------------+---------+---------+--------------------+----------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+--------------------+-------+--------+-------------------------+---------+---------+--------------------+----------+-------------+ | 1 | PRIMARY | l | range | PRIMARY,mdl_log_tim_ix | PRIMARY | 8 | NULL | 99962199 | Using where | | 2 | DEPENDENT SUBQUERY | c | eq_ref | PRIMARY,mdl_cour_cat_ix | PRIMARY | 8 | xray2qasb.l.course | 1 | Using where | +----+--------------------+-------+--------+-------------------------+---------+---------+--------------------+----------+-------------+ 

嘗試將類別選擇移到JOIN之外。 在這里,我將其放在IN()中,引擎將在連續運行時對其進行緩存。 我沒有2億行要測試,所以YMMV。

DESCRIBE 

SELECT l.id,
   l.userid AS participantid,
   l.course AS courseid,
   l.time,
   l.ip,
   l.action,
   l.info,
   l.module,
   l.url
FROM   mdl_log l   
WHERE 
  l.id > 1234567890
  AND
  l.time > 1234567890
  AND 
  l.course IN (SELECT c.id FROM mdl_course c WHERE c.category > 0)      
ORDER BY l.id ASC
LIMIT 0,200;

(除了使用EXISTS ...)

  l.id > 123456 AND l.time > 1234

似乎乞求二維索引。

99962199桌子很大,對嗎?

考慮在mdl_logtime PARTITION BY RANGE 但...

  • 分區不超過50個; 其他效率低下的問題也隨之而來。
  • 分區可能不會幫助idtime是八九不離十步調一致。 典型情況: idAUTO_INCREMENTtime大約是INSERT的時間。

如果適用,請考慮:

PRIMARY KEY(time, id)  -- see below
INDEX(id)              -- Yes, this is sufficient for `id AUTO_INCREMENT`.

有了這些索引,您可以有效地

WHERE time > ...
ORDER BY time, id

這可能是您真正想要的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM