MySQL JOIN性能极差

时间:2012-02-17 16:27:47

标签: mysql

我整天忙着试图找出为什么我的查询性能很糟糕。它非常简单,但执行时间可能超过15分钟(我在该阶段中止查询)。我正在加入一张有超过200万条记录的表格。

这是选择

SELECT
    audit.MessageID, alerts.AlertCount
FROM
    audit
LEFT JOIN (
        SELECT MessageID, COUNT(ID) AS 'AlertCount'
        FROM alerts
        GROUP BY MessageID
    ) AS alerts ON alerts.MessageID = audit.MessageID

这是EXPLAIN

| id | select_type | table      | type  | possible_keys | key                  | key_len | ref  | rows    | filtered | Extra       |
|  1 | PRIMARY     | AL         | index | NULL          | IDX_audit_MessageID  | 4       | NULL | 2330944 |   100.00 | Using index |
|  1 | PRIMARY     | <derived2> | ALL   | NULL          | NULL                 | NULL    | NULL |  124140 |   100.00 |             |
|  2 | DERIVED     | alerts     | index | NULL          | IDX_alerts_MessageID | 5       | NULL |  124675 |   100.00 | Using index |

这是架构

# Not joining, just showing types
CREATE TABLE messages (
    ID                  int NOT NULL AUTO_INCREMENT,
    MessageID           varchar(255) NOT NULL,
    PRIMARY KEY (ID),
    INDEX IDX_messages_MessageID (MessageID)
);

# 2,324,931 records
CREATE TABLE audit (
    ID                  int NOT NULL AUTO_INCREMENT,
    MessageID           int NOT NULL,
    LogTimestamp        timestamp NOT NULL,
    PRIMARY KEY (ID),
    INDEX IDX_audit_MessageID (MessageID),
    CONSTRAINT FK_audit_MessageID FOREIGN KEY(MessageID) REFERENCES messages(ID)
);

# 124,140
CREATE TABLE alerts (
    ID                  int NOT NULL AUTO_INCREMENT,
    AlertLevel          int NOT NULL,
    Text                nvarchar(4096) DEFAULT NULL,
    MessageID           int DEFAULT 0,
    PRIMARY KEY (ID),
    INDEX IDX_alert_MessageID (MessageID),
    CONSTRAINT FK_alert_MessageID FOREIGN KEY(MessageID) REFERENCES messages(ID)
);

需要注意的一些非常重要的事项 - 在“审核”或“提醒”中,MessageID不是1:1; MessageID可以存在于一个表中,但不能存在于另一个表中,或者可能存在于两个表中(这是我加入的目的);在我的测试数据库中,两者都存在MessageID的 none 。换句话说,我的查询将返回230万条记录,其中0为计数。

另外需要注意的是,'audit'和'alert'表用于将MessageID用作varchar(255)。我创建了'messages'表,希望它可以修复连接。它实际上使它更糟。以前,它需要78秒,现在,它永远不会返回。

我对MySQL缺少什么?

2 个答案:

答案 0 :(得分:1)

MySQL引擎优化子查询非常困难。尝试:

SELECT
    audit.MessageID, COUNT(alerts.ID) AS AlertCount
FROM
    audit
LEFT JOIN alerts ON alerts.MessageID = audit.MessageID
GROUP BY audit.MessageID

答案 1 :(得分:1)

您正在加入子查询。

子查询结果实际上是一个临时表 - 请注意查询执行计划中的<derived2>。正如你在那里看到的那样,它们没有被编入索引,因为它们是短暂的。

您应该将查询作为具有连接的单个单元执行,而不是加入第二个查询的结果。

编辑:安德鲁已经发布了an answer一个例子,说明如何在普通的连接查询中完成工作,而不是分两步。