需要优化mysql查询

6ss1mwsb  于 2021-06-17  发布在  Mysql
关注(0)|答案(2)|浏览(431)

我有一个查询需要一天多的时间才能完成,在temp\u message\u split表中有1700128条记录,所以请帮助我解释这个问题
下面提供了create table语句和explain plan。

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\u message\u split表中处理1700128要花一天多的时间,我们需要调整它,使它尽可能地占用最少的时间。

dxxyhpgq

dxxyhpgq1#

我猜你想把状态设置为 D 除了最高价值的 temp_message_split_key 对于每个 DH_MEMBER_ID .
最好的解决办法是 NOT EXISTS ,但mysql不支持 NOT EXISTS 在同一张table上 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 <> '' .

bejyjqdl

bejyjqdl2#

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

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 <> ''

您能否帮助我理解查询中如此微小的更改是如何导致查询性能如此巨大的差异的。

相关问题