慢速运行MySQL查询 - 如何优化?

时间:2017-09-22 13:44:15

标签: mysql sql query-optimization

我正在运行一个查询,并且正在努力理解MySQL为什么这么慢地运行它。我在下面的3个场景中包含了实际查询和EXPLAIN。

第一个查询运行速度非常慢(23秒),但从where子句中只删除一个字段(查询2)会导致查询在0.010秒内运行。这是一个TinyInt字段(基本上存储了布尔值0/1)。

项目 - > hasMany - >里程碑 - > hasMany - >任务

task_wishlist是任务和愿望清单之间的数据透视表

QUERY 1

        <Directory Id="TARGETDIR" Name="SourceDir">
            <Directory Id="ProgramFilesFolder">
                <Directory Id="INSTALLFOLDER" Name="DataBaseds_Service_Installer" />
            </Directory>
        </Directory>

    <ComponentGroup Id="Components" Directory="INSTALLFOLDER">

      ...some components  
    </ComponentGroup>

    <DirectoryRef Id="TARGETDIR">
      <Component Id="MYSQL_PASSWORD" Guid="..."
        <Environment Id=HERE YOU CAN ADD THIS  :)/>
      </Component>      
    </DirectoryRef>

查询1 EXPLAIN:

SELECT `tasks`.*,
        task_wishlist.description AS item_description,
        task_wishlist.created_at AS item_created_at
FROM `tasks`
LEFT JOIN `task_wishlist` ON `tasks`.`id` = `task_wishlist`.`task_id`
LEFT JOIN `milestones` ON `tasks`.`milestone_id` = `milestones`.`id`
LEFT JOIN `projects` ON `milestones`.`project_id` = `projects`.`id`
WHERE `task_wishlist`.`wishlist_id` = '527021'
  AND `tasks`.`active` = '1'
  AND `projects`.`active` = '1'
  AND `milestones`.`active` = '1'
ORDER BY `task_wishlist`.`created_at` DESC
LIMIT 25;
/* Affected rows: 0  Found rows: 25  Warnings: 0  Duration for 1 query: 23.072 sec. (+ 0.040 sec. network) */

查询2(删除里程碑。活动)

*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: projects
   partitions: NULL
         type: ALL
possible_keys: PRIMARY
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 997
     filtered: 10.00
        Extra: Using where; Using temporary; Using filesort
*************************** 2. row ***************************
           id: 1
  select_type: SIMPLE
        table: milestones
   partitions: NULL
         type: ref
possible_keys: PRIMARY,milestones_project_id_foreign
          key: milestones_project_id_foreign
      key_len: 4
          ref: fusion.projects.id
         rows: 3
     filtered: 10.00
        Extra: Using index condition; Using where
*************************** 3. row ***************************
           id: 1
  select_type: SIMPLE
        table: tasks
   partitions: NULL
         type: ref
possible_keys: PRIMARY,tasks_milestone_id_foreign
          key: tasks_milestone_id_foreign
      key_len: 5
          ref: fusion.milestones.id
         rows: 5
     filtered: 10.00
        Extra: Using where
*************************** 4. row ***************************
           id: 1
  select_type: SIMPLE
        table: task_wishlist
   partitions: NULL
         type: ref
possible_keys: task_wishlist_wishlist_id_foreign,task_wishlist_task_id_foreign
          key: task_wishlist_task_id_foreign
      key_len: 4
          ref: fusion.tasks.id
         rows: 100
     filtered: 0.28
        Extra: Using where
4 rows in set, 1 warning (0.01 sec)

查询2解释:

SELECT `tasks`.*,
       task_wishlist.description AS item_description,
       task_wishlist.created_at AS item_created_at
FROM `tasks`
LEFT JOIN `task_wishlist` ON `tasks`.`id` = `task_wishlist`.`task_id`
LEFT JOIN `milestones` ON `tasks`.`milestone_id` = `milestones`.`id`
LEFT JOIN `projects` ON `milestones`.`project_id` = `projects`.`id`
WHERE `task_wishlist`.`wishlist_id` = '527021'
  AND `tasks`.`active` = '1'
  AND `projects`.`active` = '1'
  /*AND `milestones`.`active` = '1'*/
ORDER BY `task_wishlist`.`created_at` DESC
LIMIT 25;
/* Affected rows: 0  Found rows: 25  Warnings: 0  Duration for 1 query: 0.028 sec. (+ 0.010 sec. network) */

查询3:删除projects.active = 1

*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: task_wishlist
   partitions: NULL
         type: ref
possible_keys: task_wishlist_wishlist_id_foreign,task_wishlist_task_id_foreign
          key: task_wishlist_wishlist_id_foreign
      key_len: 4
          ref: const
         rows: 7224
     filtered: 100.00
        Extra: Using index condition; Using where; Using filesort
*************************** 2. row ***************************
           id: 1
  select_type: SIMPLE
        table: tasks
   partitions: NULL
         type: eq_ref
possible_keys: PRIMARY,tasks_milestone_id_foreign
          key: PRIMARY
      key_len: 4
          ref: fusion.task_wishlist.task_id
         rows: 1
     filtered: 10.00
        Extra: Using where
*************************** 3. row ***************************
           id: 1
  select_type: SIMPLE
        table: milestones
   partitions: NULL
         type: eq_ref
possible_keys: PRIMARY,milestones_project_id_foreign
          key: PRIMARY
      key_len: 4
          ref: fusion.tasks.milestone_id
         rows: 1
     filtered: 100.00
        Extra: NULL
*************************** 4. row ***************************
           id: 1
  select_type: SIMPLE
        table: projects
   partitions: NULL
         type: eq_ref
possible_keys: PRIMARY
          key: PRIMARY
      key_len: 4
          ref: fusion.milestones.project_id
         rows: 1
     filtered: 10.00
        Extra: Using where
4 rows in set, 1 warning (0.00 sec)

查询3解释

SELECT `tasks`.*,
       task_wishlist.description AS item_description,
       task_wishlist.created_at AS item_created_at
FROM `tasks`
LEFT JOIN `task_wishlist` ON `tasks`.`id` = `task_wishlist`.`task_id`
LEFT JOIN `milestones` ON `tasks`.`milestone_id` = `milestones`.`id`
LEFT JOIN `projects` ON `milestones`.`project_id` = `projects`.`id`
WHERE `task_wishlist`.`wishlist_id` = '527021'
  AND `tasks`.`active` = '1'
  /*AND `projects`.`active` = '1'*/
  AND `milestones`.`active` = '1'
ORDER BY `task_wishlist`.`created_at` DESC
LIMIT 25;
/* Affected rows: 0  Found rows: 25  Warnings: 0  Duration for 1 query: 0.027 sec. (+ 4.031 sec. network) */

如何使此查询在包含所有*************************** 1. row *************************** id: 1 select_type: SIMPLE table: task_wishlist partitions: NULL type: ref possible_keys: task_wishlist_wishlist_id_foreign,task_wishlist_task_id_foreign key: task_wishlist_wishlist_id_foreign key_len: 4 ref: const rows: 7224 filtered: 100.00 Extra: Using index condition; Using where; Using filesort *************************** 2. row *************************** id: 1 select_type: SIMPLE table: tasks partitions: NULL type: eq_ref possible_keys: PRIMARY,tasks_milestone_id_foreign key: PRIMARY key_len: 4 ref: fusion.task_wishlist.task_id rows: 1 filtered: 10.00 Extra: Using where *************************** 3. row *************************** id: 1 select_type: SIMPLE table: milestones partitions: NULL type: eq_ref possible_keys: PRIMARY key: PRIMARY key_len: 4 ref: fusion.tasks.milestone_id rows: 1 filtered: 10.00 Extra: Using where *************************** 4. row *************************** id: 1 select_type: SIMPLE table: projects partitions: NULL type: eq_ref possible_keys: PRIMARY key: PRIMARY key_len: 4 ref: fusion.milestones.project_id rows: 1 filtered: 100.00 Extra: Using index 4 rows in set, 1 warning (0.00 sec) 字段的情况下正常运行?

2 个答案:

答案 0 :(得分:2)

您需要重申的一个事实是,您的查询实际上是在做简单的INNER JOIN而不是LEFT JOINS。
如果join-condition在&#34; right&#34;中没有找到任何匹配的行,Left outer join基本上与内连接exept一样工作,它返回NULL。表。
但是查询中的所有NULL都会被WHERE子句中的条件过滤掉,请仔细检查查询:

FROM `tasks`
LEFT JOIN `task_wishlist` ON `tasks`.`id` = `task_wishlist`.`task_id`
LEFT JOIN `milestones` ON `tasks`.`milestone_id` = `milestones`.`id`
LEFT JOIN `projects` ON `milestones`.`project_id` = `projects`.`id`
WHERE `task_wishlist`.`wishlist_id` = '527021'  
  AND `tasks`.`active` = '1'  
  AND `milestones`.`active` = '1'
  AND `projects`.`active` = '1' 
    来自LEFT JOIN task_wishlist
  • null由WHERE task_wishlist.wishlist_id = '527021'
  • 过滤掉 来自LEFT JOIN milestones
  • null由WHERE milestones.active = '1'
  • 过滤掉 来自LEFT JOIN projects
  • null由WHERE projects.active = '1'
  • 过滤掉

MySql知道这一点,并在内部将您的查询重写为INNER JOIN查询:

FROM `tasks`
INNER JOIN `task_wishlist` ON `tasks`.`id` = `task_wishlist`.`task_id`
INNER JOIN `milestones` ON `tasks`.`milestone_id` = `milestones`.`id`
INNER JOIN `projects` ON `milestones`.`project_id` = `projects`.`id`
WHERE `task_wishlist`.`wishlist_id` = '527021'  
  AND `tasks`.`active` = '1'  
  AND `milestones`.`active` = '1'
  AND `projects`.`active` = '1' 

如果从WHERE子句中删除(注释)某些条件,则此&#34;激活&#34;对应的LEFT JOIN是查询的一部分,因此查询的语义发生了变化,它的结果集也发生了变化。比较这些查询的性能没有任何意义,因为它们坦率地不同,事实上你正在将苹果与橙子进行比较。

由于查询正在执行简单的INNER JOIN,您可以将其重写为:

FROM `tasks`
INNER JOIN `task_wishlist` ON `tasks`.`id` = `task_wishlist`.`task_id` 
                                         AND `task_wishlist`.`wishlist_id` = '527021' 

INNER JOIN `milestones` ON `tasks`.`milestone_id` = `milestones`.`id` 
                                                AND `milestones`.`active` = '1'

INNER JOIN `projects` ON `milestones`.`project_id` = `projects`.`id` 
                                                 AND `projects`.`active` = '1' 

现在在上面的条件中你可以很容易地看到&#34;对&#34;在连接期间使用的列:

  • projects.id + projects.active
  • milestones.id + milestones.active
  • task_wishlist.task_id + task_wishlist.wishlist_id

尝试在那里添加多个索引&#34;对&#34;,projects表的一个示例:

CREATE INDEX xxx ON projects( id, active )

如果id列也是主键,则可能只能在单active列而不是多列(id, active)添加索引,因为in InnoDB, each record in a secondary index contains the primary key columns for the row, as well as the columns specified for the secondary index. 你必须自己试验。

答案 1 :(得分:1)

您说task_wishlisttaskswishlists之间的多对多映射表吗?我敢打赌它没有像样的指数。按照这里的提示:

http://mysql.rjweb.org/doc.php/index_cookbook_mysql#many_to_many_mapping_table

如果这还不够,那么请为每个表返回SHOW CREATE TABLE