需要调优mysql查询

时间:2019-01-02 13:13:30

标签: mysql sql query-optimization mysql-5.7

我有一个查询需要花费超过一天的时间来完成temp_message_split表中的1700128条记录,因此请帮助您进行调整

下面提供了创建表语句并解释计划。

UPDATE TEMP_MESSAGE_SPLIT t1 , TEMP_MESSAGE_SPLIT t2
    SET t1.STATUS = 'D'
    WHERE
    (t1.temp_message_split_key < t2.temp_message_split_key AND t1.DH_MEMBER_ID = t2.DH_MEMBER_ID)
    AND  nullif(t1.dh_member_id,'') IS NOT NULL;

这里是创建表DDL

CREATE TABLE
temp_message_split
(
    FIRST_NAME VARCHAR(20),
    LAST_NAME VARCHAR(30),
    DOB VARCHAR(10),
    EMPLOYEE_ID VARCHAR(20),
    CES_CUST_NUM VARCHAR(7),
    MED_POLICY_NUM VARCHAR(20),
    EMAIL_ADDR VARCHAR(50),
    DH_MEMBER_ID VARCHAR(9),
    ALT_ID VARCHAR(20),
    DRSN VARCHAR(2),
    SSN VARCHAR(9),
    EPIPHANY_MEMBER_ID VARCHAR(18),
    PORTAL_ADDRESS VARCHAR(30),
    STATEMENT_VENDOR VARCHAR(20),
    CONTENT_KEY VARCHAR(18),
    EPIPHANY_COMMUNICATION_ID VARCHAR(200),
    PRIORITY VARCHAR(4),
    DAYS_UNTIL_EXPIRED VARCHAR(4),
    CONTENT_DTL_KEY VARCHAR(18),
    STATUS VARCHAR(1),
    ACTIVATION_MEMBER_KEY bigint,
    MESSAGE_BOARD_KEY bigint,
    PORTAL_STATEMENT_LOC_KEY bigint,
    temp_message_split_KEY bigint NOT NULL AUTO_INCREMENT,
    PRIMARY KEY (temp_message_split_KEY),
    INDEX EPIPHANY_COMMUNICATION_ID_IDX (EPIPHANY_COMMUNICATION_ID),
    INDEX TEMP_MESSAGE_SPLIT_IDX2 (ALT_ID),
    INDEX TEMP_MESSAGE_SPLIT_IDX3 (DRSN),
    INDEX TEMP_MESSAGE_SPLIT_IDX4 (ALT_ID, DRSN),
    INDEX TEMP_MESSAGE_SPLIT_IDX1 (DH_MEMBER_ID)
)
ENGINE=InnoDB DEFAULT CHARSET=utf8;

这是它的解释计划:

+----+-------------+-------+------------+-------+---------------------------------+-------------------------+---------+------+---------+----------+------------------------------------------------+
| id | select_type | table | partitions | type  | possible_keys                   | key                     | key_len | ref  | rows    | filtered | Extra                                          |
+----+-------------+-------+------------+-------+---------------------------------+-------------------------+---------+------+---------+----------+------------------------------------------------+
|  1 | SIMPLE      | t2    | NULL       | index | PRIMARY,TEMP_MESSAGE_SPLIT_IDX1 | TEMP_MESSAGE_SPLIT_IDX1 | 30      | NULL | 1619639 |   100.00 | Using index                                    |
|  1 | UPDATE      | t1    | NULL       | ALL   | PRIMARY,TEMP_MESSAGE_SPLIT_IDX1 | NULL                    | NULL    | NULL | 1619639 |    33.33 | Range checked for each record (index map: 0x5) |
+----+-------------+-------+------------+-------+---------------------------------+-------------------------+---------+------+---------+----------+------------------------------------------------+
2 rows in set (0.00 sec)

此查询需要花费超过一天的时间来处理temp_message_split表中的1700128,我们需要对其进行调整,以使其花费尽可能多的时间。时间。

2 个答案:

答案 0 :(得分:1)

我的最佳猜测是,您希望将每个D的状态(除了最高值temp_message_split_key都设置为DH_MEMBER_ID

最好的解决方案是NOT EXISTS,但是MySQL在NOT EXISTS查询的同一表上不支持UPDATE

因此,另一种方法使用GROUP BY

UPDATE TEMP_MESSAGE_SPLIT t1 JOIN
       (SELECT t2.DH_MEMBER_ID, MAX(t2.temp_message_split_key) as max_temp_message_split_key
        FROM TEMP_MESSAGE_SPLIT t2
        GROUP BY t2.DH_MEMBER_ID
       ) t2
       ON t1.DH_MEMBER_ID = t2.DH_MEMBER_ID AND
          t1.temp_message_split_key < t2.max_temp_message_split_key
    SET t1.STATUS = 'D';

(dh_member_id, temp_message_split_key)上的索引可能会提高性能。

这仍然需要很长时间,因为(大概)您正在更新很多行。如果可能的话,仅使用所需的值创建一个新表可能会更简单。这样会更快(由于记录和锁定)。

NULLIF()可能什么也不做,但是对查询性能的影响很小。最好写成t1.dh_member_id <> ''

答案 1 :(得分:0)

感谢Gordan的最后评论。我已经基于此更新了查询,现在它可以在几秒钟内完成。这是我的新查询:

UPDATE TEMP_MESSAGE_SPLIT t1 , TEMP_MESSAGE_SPLIT t2
    SET t1.STATUS = 'D'
    WHERE
    (t1.temp_message_split_key < t2.temp_message_split_key AND t1.DH_MEMBER_ID = t2.DH_MEMBER_ID)
    AND t1.DH_MEMBER_ID <> ''

您能否帮助我理解查询的这种微小变化如何导致查询性能的巨大差异。