【问题标题】:Mysql Query performance very slowMysql查询性能很慢
【发布时间】:2018-09-07 05:56:48
【问题描述】:

以下查询耗时超过 8 分钟,处理了 900 000 行。它非常慢并且影响我的产品。我无法确定查询变慢的原因,所有索引都设置得很好。

explain SELECT 
    COUNT(DISTINCT (cinfo.CONTACT_ID))
FROM
    cinfo
        INNER JOIN
    LTocMapping ON cinfo.CONTACT_ID = LTocMapping.CONTACT_ID
WHERE
    (((((((((cinfo.COUNTRY LIKE '%Panama%')
        OR (cinfo.COUNTRY LIKE '%PANAMA%'))
        AND (((cinfo.CONTACT_EMAIL NOT LIKE '%test%')
        AND (cinfo.CONTACT_EMAIL NOT LIKE '%engine%'))
        OR (cinfo.CONTACT_EMAIL IS NULL)))
        AND ((SELECT 
            (GROUP_CONCAT(Temp.LIST_ID
                    ORDER BY Temp.LIST_ID) REGEXP ('.*,*221715000514445053,*.*$'))
            FROM
                LTocMapping Temp
            WHERE
                ((LTocMapping.CONTACT_ID = Temp.CONTACT_ID)
                    AND (((Temp.MAPPING_ID >= 221715000000000000)
                    AND (Temp.MAPPING_ID <= 221715999999999999))
                    OR ((Temp.MAPPING_ID >= 0)
                    AND (Temp.MAPPING_ID <= 999999999999))))
            GROUP BY Temp.CONTACT_ID) = '0'))
        AND ((SELECT 
            (GROUP_CONCAT(Temp.LIST_ID
                    ORDER BY Temp.LIST_ID) REGEXP ('.*,*221715000520574130,*.*$'))
            FROM
                LTocMapping Temp
            WHERE
                ((LTocMapping.CONTACT_ID = Temp.CONTACT_ID)
                    AND (((Temp.MAPPING_ID >= 221715000000000000)
                    AND (Temp.MAPPING_ID <= 221715999999999999))
                    OR ((Temp.MAPPING_ID >= 0)
                    AND (Temp.MAPPING_ID <= 999999999999))))
            GROUP BY Temp.CONTACT_ID) = '0'))
        AND (LTocMapping.LIST_ID IN (221715000520574130 , 221715000201569885)))
        AND (LTocMapping.STATUS = BINARY 'subscribed'))
        AND (((cinfo.CONTACT_STATUS = BINARY 'active')
        OR (cinfo.CONTACT_STATUS = BINARY 'softbounce'))
        AND (LTocMapping.STATUS = BINARY 'subscribed')))
        AND (((cinfo.CONTACT_ID >= 221715000000000000)
        AND (cinfo.CONTACT_ID <= 221715999999999999))
        OR ((cinfo.CONTACT_ID >= 0)
        AND (cinfo.CONTACT_ID <= 999999999999))))

答案是

下表FYR

表 1:

mysql> desc cinfo;
+------------------------+--------------+------+-----+-----------+-------+
| Field                  | Type         | Null | Key | Default   | Extra |
+------------------------+--------------+------+-----+-----------+-------+
| CONTACT_ID             | bigint(19)   | NO   | PRI | NULL      |       |
| CONTACT_EMAIL          | varchar(100) | NO   | MUL | NULL      |       |
| TITLE                  | varchar(20)  | YES  |     | NULL      |       |
| FIRSTNAME              | varchar(100) | YES  |     | NULL      |       |
| LASTNAME               | varchar(50)  | YES  |     | NULL      |       |     |
| ADDED_BY               | varchar(20)  | YES  |     | NULL      |       |
| ADDED_TIME             | bigint(19)   | NO   |     | NULL      |       |
| LAST_UPDATED_TIME      | bigint(19)   | NO   |     | NULL      |       |
+------------------------+--------------+------+-----+-----------+-------+

表 2:

 mysql> desc LTocMapping;
+---------------------+--------------+------+-----+------------+-------+
| Field               | Type         | Null | Key | Default    | Extra |
+---------------------+--------------+------+-----+------------+-------+
| MAPPING_ID          | bigint(19)   | NO   | PRI | NULL       |       |
| CONTACT_ID          | bigint(19)   | NO   | MUL | NULL       |       |
| LIST_ID             | bigint(19)   | NO   | MUL | NULL       |       |
| STATUS              | varchar(100) | YES  |     | subscribed |       |
| MAPPING_STATUS      | varchar(20)  | YES  |     | connected  |       |
| MAPPING_TIME        | bigint(19)   | YES  |     | NULL       |       |
+---------------------+--------------+------+-----+------------+-------+

【问题讨论】:

  • 您是否真的在编写/查看看起来像那样的 SQL,或者您是否只是认为否则我们很难为您提供帮助?
  • Bug:在搜索LIKE %Guinea% 时,您还会得到“Guinea-Bissau”。尼日尔和尼日利亚也是如此。可能还有其他人。

标签: mysql performance query-performance


【解决方案1】:

据我所知,您的子查询是瓶颈:

  • 对于第一个子查询,您使用的是LTocMapping.CONTACT_ID
  • 对于第二个子查询,您也在使用LTocMapping.CONTACT_ID

这些引用(对外部查询的值)导致这些内部查询变为correlated subqueries(也称为dependent subqueries)。这意味着:对于您要在其中一个外部表(~970000)上获取的每一行 - 您正在另一个表上触发 2 个额外的查询。

所以,您正在执行的查询是 180 万个(看起来也不是微不足道的)。

大多数时候,相关的子查询可以被适当的连接替换。但这取决于用例。当使用不同的别名时,您还可以将同一个表连接两次。

但要概述一些连接选项,您需要解释为什么导致条件 group_concat(....) = '0' 的子查询很重要 - 或者更好的是,您想要实现的目标。

(ps.:您还可以看到,explain 将它们概述为dependent subquery

【讨论】:

    【解决方案2】:

    OR效率低下,看看能不能避免。

    LIKE 中的前导通配符效率低下。看看FULLTEXT 索引是否适合您。

    使用正确的COLLATION,您无需同时测试大小写。你也可以避免使用BINARY。在这两种情况下,您都可以使用索引。 (你有什么索引?)

    尝试改变

    WHERE ( ( SELECT ... ) = '0' )
    

    WHERE ( NOT EXISTS ( SELECT ... ) )
    

    SELECT 需要一些修改。)

    (请去掉一些多余的括号;它很难阅读。)

    (请使用SHOW CREATE TABLE;它比DESCRIBE更具描述性。)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-03-15
      • 1970-01-01
      • 1970-01-01
      • 2016-03-09
      相关资源
      最近更新 更多