正确的 MySQL Left Join Query 会使小型数据库负担过重,尽管对行进行了索引

A correct MySQL Left Join Query overburdens a smallsized DB although rows are indexed

我有这个有效的 SQL 查询,但它几乎使我的数据库崩溃:

SELECT MASTER.master_id, 
       MASTER.master_summary, 
       MASTER.master_start, 
       MASTER.master_end, 
       MASTER.master_risk, 
       MASTER.master_source, 
       MASTER.master_veto, 
       master.master_tags, 
       NULL AS HAS_CE, 
       C2C.c2c_customer 
FROM      `cer_master` MASTER 
LEFT JOIN `cer_c2customer` C2C 
              ON ( C2C.c2c_id = MASTER.master_id AND C2C.c2c_source = MASTER.master_source ) 

WHERE  ( MASTER.master_id NOT LIKE 'TAV%' ) 
        AND (( MASTER.master_class <> 'type2' ) OR ( MASTER.master_class <> 'type3' )) 
        AND ( MASTER.master_status <> 'Cancelled' ) 
        AND ( MASTER.master_end >= Now() AND MASTER.master_start >= Date_sub(Now(), INTERVAL 1 day) ) 

如果我尝试 运行 在 phpMyAdmin 上执行此操作,我必须等待 5 分钟并得到此结果:总共 3,699,查询耗时 0.9358 秒

我已将 MASTER.master_id, MASTER.master_start, MASTER.master_end, MASTER.master_sourcec2c.c2c_id, C2C.c2c_source and C2C.c2c_customer 编入索引,但似乎没有帮助。

附加信息:cer_master MASTER table 有 277,502 行,cer_c2customer C2C table 有 72,788 行。

有人可以帮我优化这个查询吗?我非常需要它,想不出别的办法。

编辑:EXPLAIN 查询的结果:

+----+-------------+--------+-------+-------------------------------------------------------+---------------------------------+---------+------+-------+-------------+
| id | select_type | table  | type  | possible_keys                                         | key                             | key_len | ref  | rows  | Extra       |
+----+-------------+--------+-------+-------------------------------------------------------+---------------------------------+---------+------+-------+-------------+
| 1  | SIMPLE      | MASTER | range | CHM_MASTER_SCHEDULED_START_DATE,CHM_MASTER_SCHEDUL... | CHM_MASTER_SCHEDULED_START_DATE | 4       | NULL | 5042  | Using where |
+----+-------------+--------+-------+-------------------------------------------------------+---------------------------------+---------+------+-------+-------------+
| 1  | SIMPLE      | C2C    | ALL   | CER_C2C_CHANGE_ID                                     | NULL                            | NULL    | NULL | 72788 |             |
+----+-------------+--------+-------+-------------------------------------------------------+---------------------------------+---------+------+-------+-------------+

Table  Create Table
master  CREATE TABLE `master` (
 `ID` int(11) NOT NULL AUTO_INCREMENT,
 ` MASTER_LAST_MODIFIED_DATE` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
 ` MASTER_SOURCE` varchar(16) NOT NULL,
 ` MASTER_ID` varchar(16) NOT NULL,
 ` MASTER_SUMMARY` text NOT NULL,
 ` MASTER_NOTES` text NOT NULL,
 ` MASTER_SERVICE` varchar(255) NOT NULL,
 ` MASTER_SITE` text NOT NULL,
 ` MASTER_START` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
 ` MASTER_END` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
 ` MASTER_DEPARTMENT_FLAG` varchar(8) NOT NULL,
 ` MASTER_RISK` int(8) NOT NULL DEFAULT '1',
 ` MASTER_IMPACT_LEVEL` varchar(64) NOT NULL,
 ` MASTER_TOOL_STATUS` varchar(32) NOT NULL,
 ` MASTER_IMPACT_RISK_NOTES` text NOT NULL,
 ` MASTER_CALENDAR_WEEK` varchar(16) NOT NULL,
 ` MASTER_TAGS` varchar(1024) NOT NULL,
 ` MASTER_VETO` tinyint(1) NOT NULL DEFAULT '0',
 ` MASTER_LAYER_TAGS` text NOT NULL,
 ` MASTER_ORAKEL_ID` int(11) NOT NULL DEFAULT '0',
 ` MASTER_USED_TEMPLATE` text NOT NULL,
 PRIMARY KEY (`ID`),
 KEY ` MASTER_ID` (` MASTER_CHANGE_ID`),
 KEY ` MASTER_LAST_MODIFIED_DATE` (` MASTER_LAST_MODIFIED_DATE`),
 KEY ` MASTER_SERVICE` (` MASTER_SERVICE`),
 KEY ` MASTER_START` (` MASTER_START`),
 KEY ` MASTER_END` (` MASTER_END_`),
 KEY ` MASTER_SOURCE` (` MASTER_SOURCE`)
) ENGINE=MyISAM AUTO_INCREMENT=278315 DEFAULT CHARSET=utf8

这是从 c2c_customer 显示创建 table: cerberus_change2customer

CREATE TABLE `c2c_customer` (
 `ID` int(11) NOT NULL AUTO_INCREMENT,
 `C2C_SOURCE` text NOT NULL,
 `C2C_ID` text NOT NULL,
 `C2C_CUSTOMER` text NOT NULL,
 `C2C_LAST_MODFIED` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
 PRIMARY KEY (`ID`),
 FULLTEXT KEY `C2C_ID` (`C2C_ID`),
 FULLTEXT KEY `C2C_CUSTOMER` (`C2C_CUSTOMER`)
) ENGINE=MyISAM AUTO_INCREMENT=516044 DEFAULT CHARSET=utf8

您的索引应该在所有列上,而不是每个列的单独索引。

例如:

ALTER TABLE `cer_c2customer` ADD INDEX `cer_c2customer_ID_SOURCE_CUSTOMER` (c2c_id, c2c_source, c2c_customer)

这意味着一个索引可用于定位数据,并在查询中提供此 table 所需的所有列。

此外,您可能希望 cer_master 上的聚类索引作为开始日期或结束日期。

一个技巧是正确地排列你的 where 子句

WHERE  ( MASTER.master_id NOT LIKE 'TAV%' ) 
        AND (( MASTER.master_class <> 'type2' ) OR ( MASTER.master_class <> 'type3' )) 
        AND ( MASTER.master_status <> 'Cancelled' ) 
        AND ( MASTER.master_end >= Now() AND MASTER.master_start >= Date_sub(Now(), INTERVAL 1 day) )

将最快且结果集最少的 where 子句上移。

例如,如果您知道此条件的结果集较小,请将其上移

( MASTER.master_status <> 'Cancelled' )

因此,查询变为

WHERE  ( MASTER.master_status <> 'Cancelled' )  
        AND (( MASTER.master_class <> 'type2' ) OR ( MASTER.master_class <> 'type3' )) 
        AND ( MASTER.master_id NOT LIKE 'TAV%' )
        AND ( MASTER.master_end >= Now() AND MASTER.master_start >= Date_sub(Now(), INTERVAL 1 day) )

按重要性排序:

  1. 修复始终为 TRUE 的 OR。 (错误修复,非优化)
  2. 添加 Ben 的复合(和覆盖)索引。
  3. 更改为 InnoDB。
  4. INDEX(master_start), INDEX(master_end) -- 是的,两个独立的,而不是一个复合的索引。优化器可能选择其中之一并受益。
  5. 按照 Shikhar 的建议,订购 WHERE 子句。 (即使完成其余的工作,这也只会提供微不足道的改进。)