【问题标题】:Select columns from table1 join table2, taking very long time从 table1 中选择列连接 table2,需要很长时间
【发布时间】:2018-01-01 12:05:52
【问题描述】:

我有 2 张桌子。从这两个表中,我尝试使用带有连接的选择查询将记录插入到第三个表中。但是我发现带有连接的选择查询不使用索引并且花费大量时间,因此插入非常慢。
我尝试按照几篇帖子中的建议创建多个索引,但没有成功。
MySQL with JOIN not using index
MySQL query with JOIN not using INDEX

这是我的表格结构:

CREATE TABLE master_table (
id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
field1 VARCHAR(50) DEFAULT NULL,
field2 VARCHAR(50) DEFAULT NULL,
field3 VARCHAR(50) DEFAULT NULL,
field4 VARCHAR(50) DEFAULT NULL,
PRIMARY KEY (id),
KEY mt_field1_index (field1)
) ENGINE=INNODB DEFAULT CHARSET=utf8;

CREATE TABLE child_table (
c_id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
m_id BIGINT(20) UNSIGNED NOT NULL ,
group_id BIGINT(20) UNSIGNED NOT NULL ,
status ENUM('Status1','Status2','Status3') NOT NULL,
job_id VARCHAR(50) DEFAULT NULL,
PRIMARY KEY (c_id),
UNIQUE KEY ct_mid_gid (m_id,group_id),
KEY Index_ct_status (status),
KEY index_ct_jobid (job_id),
KEY index_ct_mid (m_id),
KEY index_ct_cid_sts_tsk (group_id,status,job_id)
) ENGINE=INNODB DEFAULT CHARSET=utf8;  

查询:


SELECT m.id
     , NULLIF(TRIM(m.field1),'')
  FROM master_table m 
  JOIN child_table c 
    ON m.id = c.m_id 
 WHERE c.group_id = 2 
   AND c.status = 'Status3' 
   AND c.job_id = 0 
 ORDER 
    BY m.id  
 LIMIT 0, 1000;

解释:


+-------+-------------+-------+------------+----------+------------------------------------------------------------------------------+-----------------+---------+--------------+-------+----------+------------------------------------------------+
|    id | select_type | table | partitions |   type   |                                possible_keys                                 |       key       | key_len |     ref      | rows  | filtered |                     Extra                      |
+-------+-------------+-------+------------+----------+------------------------------------------------------------------------------+-----------------+---------+--------------+-------+----------+------------------------------------------------+
|     1 | SIMPLE      |    c  |    (NULL)  |   ref    |  ct_mid_gid,Index_ct_status,index_ct_jobid,index_ct_mid,index_ct_cid_sts_tsk | Index_ct_status |       1 | const        | 65689 |     0.00 | Using where; Using temporary; Using filesort   |
|     1 | SIMPLE      |    m  |    (NULL)  |   eq_ref | PRIMARY                                                                      | PRIMARY         |       8 | r_n_d.c.m_id |     1 |   100.00 | (NULL)                                         |
+-------+-------------+-------+------------+----------+------------------------------------------------------------------------------+-----------------+---------+--------------+-------+----------+------------------------------------------------+

【问题讨论】:

    标签: mysql join indexing


    【解决方案1】:

    您是否为 where 子句中使用的列创建了索引? 通过在列上添加索引来检查查询。

    但请记住,如果您添加索引,那么对表的插入、更新和删除操作可能会变慢。

    【讨论】:

    • KEY index_ct_cid_sts_tsk (group_id,status,job_id),已经存在了。
    • @money - 接近,但还不够;看我的回答。
    【解决方案2】:
     WHERE c.group_id = 2 
       AND c.status = 'Status3' 
       AND c.job_id = 0 
     ORDER BY c.m_id     -- Note the change
    

    需要

     INDEX(group_id, status, job_id,   -- in any order
           m_id)                       -- last
    

    您拥有的(单独的索引)相同。

    为了到达LIMIT,索引必须完全超过WHEREORDER BY。这可以防止计算所有行(在LIMIT 之前)和排序,然后才执行LIMIT

    因此,您获得了 4 次加速:

    • 索引有效地获取所需的行(来自c
    • 不需要排序通道(因为ORDER BY 按顺序交付它们)
    • 索引“覆盖”(因此,c 的索引 BTree 和数据 BTree 之间没有来回弹跳)
    • 在 1000 处停车。

    当您使用它时,请考虑摆脱AUTO_INCREMENT。折腾c_id 并改变

    PRIMARY KEY (c_id),
    UNIQUE KEY ct_mid_gid (m_id, group_id)
    

    -->

    PRIMARY KEY(m_id, group_id)
    

    巧合的是,如果您这样做了,您的KEY index_ct_cid_sts_tsk (group_id,status,job_id) 会偶然发现完美的索引。这是因为 PK 被隐式附加到任何二级索引上,但您需要 m_idnot c_id。无论如何,我更喜欢明确。

    并且在进行更改时,丢弃所有冗余索引。例如,KEY index_ct_mid (m_id) 是无用的,因为它是另一个索引的开始。

    【讨论】:

    • 感谢您的详细回答。一旦我在 order by 子句中使用 c.m_id 而不是 m.id,它就停止使用 filesort 和临时,并在添加建议的索引时开始使用带有 where 的索引。虽然我仍然想知道,如果我在 where 子句中有类似 m.field1 = 'xyz' 的内容以及当前条件,我应该将什么用于 order by 子句。我的意思是,何时使用 c.m_id 以及何时使用 m.id
    • 如果我必须对存在于master_table 的列进行排序。类似order by m.field1, c.m_id
    • 一些的情况下,优化器会注意到c.m_idm.id是一样的,但我猜在这种情况下不会。
    • ORDER BY 要使用索引,它需要查看 same 表中的列和 same 方向(ASC/DESC)。 (后一个限制可以在 8.0 中解决。)
    • 嗨@Rick,正如我在第一条评论中提到的那样,本地机器上的事情得到了改进。但是当我在生产中推送更改时,它仍然使用文件排序和临时(现在在主表上)以及在哪里(在子表上)根本不使用索引。你能在这里帮我理解为什么它在本地设置和生产中表现不同吗?在本地我使用的是 mysql 5.7.12,认为这可能是由于 mysql 版本,我将生产上的 mysql 从 5.6 升级到 5.7.19 但无济于事。本地表在生产中大约有 1.5L(两个表)记录,它有大约 10L(主)、30L(子)记录。
    猜你喜欢
    • 2014-09-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多