【问题标题】:Sending data taking too long but indexes aready create发送数据耗时过长但索引已创建
【发布时间】:2017-09-13 21:41:32
【问题描述】:

我的查询需要 20 秒才能返回结果:(

在表 case 和 case_cstm 中,我有 960,000 行

这是我的查询:

    SELECT  cases.id ,cases_cstm.assunto_c, cases.name , cases.case_number ,
      cases.priority  , accounts.name account_name , 
      accounts.assigned_user_id account_name_owner  , 
      'Accounts' account_name_mod, cases.account_id  , 
      LTRIM(RTRIM(CONCAT(IFNULL(jt1.first_name,''),' ',IFNULL(jt1.last_name,'')))) assigned_user_name , 
      jt1.created_by assigned_user_name_owner  , 
      'Users' assigned_user_name_mod, cases.status , cases.date_entered ,
      cases.assigned_user_id  
   FROM cases  
   LEFT JOIN cases_cstm ON cases.id = cases_cstm.id_c
   LEFT JOIN  accounts accounts ON 
      cases.account_id=accounts.id AND accounts.deleted=0   AND 
      accounts.deleted=0  
   LEFT JOIN  users jt1 ON 
      cases.assigned_user_id=jt1.id AND 
      jt1.deleted=0   AND jt1.deleted=0 
   where 
     (((LTRIM(RTRIM(CONCAT(IFNULL(accounts.name,'')))) LIKE 'rodrigo fernando%' OR 
     LTRIM(RTRIM(CONCAT(IFNULL(accounts.name,'')))) LIKE 'rodrigo fernando%'))) AND 
     cases.deleted=0 ORDER BY cases.date_entered DESC LIMIT 0,11;

这是表的索引:

+-------+------------+--------------------+--------------+------------------
+-----------+-------------+----------+--------+------+------------+--------
| Table | Non_unique | Key_name           | Seq_in_index | Column_name |Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment 
+-------+------------+--------------------+--------------+------------------
+-----------+-------------+----------+--------+------+------------+---------
| cases |          0 | PRIMARY            |            1 | id          | A         |      911472 |     NULL | NULL   |      | BTREE      |          
| cases |          0 | case_number        |            1 | case_number | A         |      911472 |     NULL | NULL   |      | BTREE      |         
|
| cases |          1 | idx_case_name      |            1 | name        | A         |      911472 |     NULL | NULL   | YES  | BTREE      |         
|
| cases |          1 | idx_account_id     |            1 | account_id  | A         |      455736 |     NULL | NULL   | YES  | BTREE      |         
|
| cases |          1 | idx_cases_stat_del |            1 | assigned_user_id| A         |         106 |     NULL | NULL   | YES  | BTREE      |         
|
| cases |          1 | idx_cases_stat_del |            2 | status      | A         |         197 |     NULL | NULL   | YES  | BTREE      |         
|
| cases |          1 | idx_cases_stat_del |            3 | deleted     | A         |         214 |     NULL | NULL   | YES  | BTREE      |         
|
| cases |          1 | idx_priority       |            1 | priority    | A         |      455736 |     NULL | NULL   | YES  | BTREE      |         
|
| cases |          1 | idx_date_entered   |            1 | date_entered| A         |      455736 |     NULL | NULL   | YES  | BTREE      |         
+-------+------------+--------------------+--------------+------------------
+-----------+-------------+----------+--------+------+------------+---------

The Explain command of query(Image!)

这是查询执行的概况:

+--------------------+-----------+
| Status             | Duration  |
+--------------------+-----------+
| starting           |  0.000122 |
| Opening tables     |  0.000180 |
| System lock        |  0.000005 |
| Table lock         |  0.000005 |
| init               |  0.000051 |
| optimizing         |  0.000017 |
| statistics         |  0.000071 |
| preparing          |  0.000021 |
| executing          |  0.000003 |
| Sorting result     |  0.000004 |
| Sending data       | 21.595455 |
| end                |  0.000012 |
| query end          |  0.000002 |
| freeing items      |  0.000419 |
| logging slow query |  0.000005 |
| logging slow query |  0.000002 |
| cleaning up        |  0.000004 |

谁能帮我理解为什么查询需要这么长时间才能执行?

谢谢!!

【问题讨论】:

  • 你在 where 子句中有很多函数调用,可能是这个问题
  • 像往常一样,Profile 会说无用的“发送数据”。
  • 这不应该花费 20 秒。其他事情正在发生。请提供SHOW CREATE TABLE cases
  • 您的查询根本不需要left join。你还有一堆重复的条件,比如accounts.deleted=0 AND accounts.deleted=0

标签: mysql database indexing


【解决方案1】:

首先,将您的 LEFT JOIN 更改为 INNER JOIN 的帐户我不知道这是否会产生巨大的变化,但如果您了解其中的区别,那就更有意义了。

您对LEFT JOIN 的意思是“我想要所有案例,无论他们是否有关联帐户”。 INNER JOIN 在这里的意思是“给我所有的案例,并为他们归还所有的账户”。

查询的最终结果是相同的,因为您稍后会使用 WHERE 子句过滤掉内容,但我觉得这 可能就是为什么 idx_account_id 是被忽略了。

第二个可能更大的问题是你的 where 子句:

 (((LTRIM(RTRIM(CONCAT(IFNULL(accounts.name,'')))) LIKE 'rodrigo fernando%' OR 
 LTRIM(RTRIM(CONCAT(IFNULL(accounts.name,'')))) LIKE 'rodrigo fernando%'))) AND

这里有很多函数,而 MySQL 无法使用索引对其进行优化。将检查每条记录的这种情况,并且将为每条记录调用您正在使用的所有函数。这很可能是最大的问题。

首先,这可以简化一点。我认为这个 OR 语句的两边是相同的,所以让我们先把它变成一个:

LTRIM(RTRIM(CONCAT(IFNULL(accounts.name,'')))) LIKE 'rodrigo fernando%'

既然您要在 switch 语句的一侧添加通配符,为什么还要使用 RTRIM?

LTRIM(CONCAT(IFNULL(accounts.name,''))) LIKE 'rodrigo fernando%'

如果只有一件事,你不需要 CONCAT 任何东西!

LTRIM(IFNULL(accounts.name,'')) LIKE 'rodrigo fernando%'

LTRIM 适用于 NULL 值

LTRIM(accounts.name) LIKE 'rodrigo fernando%'

好的,这为我们节省了很多功能。然而,最后一个 LTRIM 仍然是一个主要问题,因为它仍然完全阻止 mysql 使用索引。解决方案相当简单:

  1. 更新您的帐户表一次:

    更新账户 SET name = LTRIM(name);

  2. 确保每次插入新帐户时,在插入前先进行修剪。所以你现在真的是在INSERT 时间做这件事,而不是SELECT 时间。

  3. 将之前的 WHERE 子句更改为:

    accounts.name LIKE 'rodrigo fernando%'

Boom,您现在可以在 accounts.name 上使用索引,它会非常快。

【讨论】:

  • 是的,这行得通!我的意思是将LEFT更改为INNER JOIN,在cases_cstm的cases和accounts的cases中,查询现在在0.9秒内执行。感谢您的帮助
【解决方案2】:

我按照 Evert 的提示解决了这个问题! 需要明确的是,这是一个系统动态挂载的查询,我仍然需要优化代码以删除在这种情况下没有意义的功能。 对我有帮助的是在 case_cstm 的 case 和 accounts 的 case 中用 INNER 连接替换 LEFT ...只有通过这种更改,查询才在 0.9 秒内开始执行!

感谢大家的帮助!

【讨论】:

    【解决方案3】:

    您还需要更改查询以及索引。由于您仅在 case 表上建立了索引,并且您正在使用 account 和 users 表,因此您在索引时也应该考虑这些表。如下更改您的查询。

    SELECT cases.id, cases_cstm.assunto_c, cases.name, cases.case_number, cases.priority, accounts.name account_name, accounts.assigned_user_id account_name_owner, 
    'Accounts' account_name_mod, cases.account_id, LTRIM(RTRIM(CONCAT(IFNULL(jt1.first_name,''),' ',IFNULL(jt1.last_name,'')))) assigned_user_name, 
    jt1.created_by assigned_user_name_owner,'Users' assigned_user_name_mod, cases.status, cases.date_entered, cases.assigned_user_id 
    FROM cases  [enter link description here][1]
    LEFT JOIN cases_cstm ON cases.id = cases_cstm.id_c
    LEFT JOIN accounts accounts ON cases.account_id=accounts.id AND accounts.deleted=0 AND (TRIM(accounts.name) LIKE 'rodrigo fernando%' OR TRIM(accounts.name) LIKE 'rodrigo fernando%')
    LEFT JOIN  users jt1 ON cases.assigned_user_id=jt1.id AND jt1.deleted=0
    WHERE cases.deleted=0 ORDER BY cases.date_entered DESC LIMIT 0,11;
    

    然后先删除没有被使用的索引,然后在表上创建索引,如下:

    1. 案例 - 已删除,date_entered(一个索引多列)
    2. 帐户 - 已删除,名称(一个索引多列)
    3. 用户 - 已删除

    创建这些索引并确保在查询中选择和使用的列的顺序必须相同,因为 MySql 使用索引的任何最左边的前缀。如果您需要更多详细信息,请访问以下链接:

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-10-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-27
      • 1970-01-01
      • 1970-01-01
      • 2016-09-13
      相关资源
      最近更新 更多