【问题标题】:mysql - long running query without proper indexesmysql - 没有适当索引的长时间运行查询
【发布时间】:2018-10-09 19:20:30
【问题描述】:

这个查询在生产中运行了大约 15 个小时,我正在寻找对此进行改进的替代方法,

我认为可能有帮助的一些改进在这里评论:

SELECT  table1.*
    FROM  table1
    WHERE  UPPER(LEFT(table1.cloumn1, 1)) IN ('A', 'B')
      AND  table1.cloumn2 = 'N' /* add composite index for cloumn2,
        column3 */
      AND  table1.cloumn3 != 'Y'
      AND  table1.id IN (
        SELECT  MAX(id)
            FROM  table1
            GROUP BY  column5,column6
                        ) /* move this clause to 2nd after
    where  */
      AND  table1.column4 IN (
        SELECT  column1
            FROM  table2
            WHERE  column2 IN ('VALUE1', 'VALUE2')
              AND  (SUBSTRING(column3,6,1) = 'Y'
                      OR  SUBSTRING(column3,25,1) = 'Y')
                          ) /* move this clause to 1st after
    where  */
      AND  (table1.column5,table1.column6) NOT IN (
        SELECT  column1, column2
            FROM  table3
            WHERE  table3.column3 IN ('A', 'B')/* add index for this column*/
           )
      AND  DATE_FORMAT(timstampColumn, '%Y/%m/%d') > DATE_ADD(CURRENT_DATE,
                INTERVAL - 28 DAY)) /* need index  ON this col? */ ;

感谢任何 cmets/建议。

更新:只更新过滤顺序,查询性能提高到~28秒,在添加一些索引和替换一些子查询到连接后会在这里更新

【问题讨论】:

  • 多个nonSARGable expressions: UPPER(LEFT(table1.cloumn1, 1)), DATE_FORMAT(timstampColumn, '%Y/%m/%d'), SUBSTRING(column3,6,1)
  • “我正在寻找改进的替代方案”,考虑使用 PostgreSQL 而不是 MySQL。PostgreSQL 支持表达式索引,这意味着您可以索引函数。postgresql.org/docs/9.1/static/indexes-expressional.html
  • 或者使用 MySQL 5.7.6+ 生成的列来索引函数。像column AS (UPPER(LEFT(table1.cloumn1, 1))) stored 这样你就可以索引了。
  • @RaymondNijland - UPPER(LEFT(table1.cloumn1, 1)) 为此,我正在考虑将索引创建为 create index name_first on my_table (cloumn1(1)); 以排除 LEFT 的使用。
  • 查询中有多余的括号吗?当我对其进行快速语法检查时似乎有。但可能最好进行连接而不是使用子查询。特别是对于获取每个 column5 / column6 的最大 id 的主要检查。这样 MySQL 可以排除最早的最大记录数。

标签: mysql performance optimization indexing query-optimization


【解决方案1】:

假设您可以添加有用的索引(这将有助于您的某些检查),那么可能会尽早尝试排除行。

我怀疑对于每个 column5 / column6 组合,table1 上有很多行。如果您可以尽早获得其中的最新信息(即,使用您加入的子查询),那么您可以在需要检查任何非索引 WHERE 子句之前从 table1 中排除大多数行。您还可以通过对 table3 上的子查询进行进一步连接来排除其中的一些。

未经测试,但如果我对您的数据库结构的假设是正确的,那么这可能是一个改进:-

SELECT table1.* 
FROM 
(
    SELECT MAX(table1.id) AS max_id
    FROM table1 
    INNER JOIN 
    (
        SELECT DISTINCT column1, column2 
        FROM table3
        WHERE table3.column3 IN ('A', 'B')
        AND DATE_FORMAT(timstampColumn, '%Y/%m/%d') > DATE_ADD(CURRENT_DATE, INTERVAL - 28 DAY)
    ) sub0_0
    ON table1.column5 = sub0_0.column1
    AND  table1.column6 = sub0_0.column2
    WHERE (table1.cloumn1 LIKE 'A%' OR table1.cloumn1 LIKE 'B%')
    AND table1.cloumn2 = 'N'
    AND table1.cloumn3 != 'Y'
    GROUP BY table1.column5,
            table1.column6
) sub0
INNER JOIN table1
ON table1.id = sub0.max_id
INNER JOIN
(
    SELECT DISTINCT column1 
    FROM table2
    WHERE column2 IN ('VALUE1', 'VALUE2') 
    AND (SUBSTRING(column3,6,1) = 'Y' 
    OR SUBSTRING(column3,25,1) = 'Y')
) sub1
ON table1.column4 = sub1.column1

【讨论】:

  • 谢谢,我会在不同的环境中测试这些并更新我的发现。
  • 甚至没有添加索引 - 我看到测试环境中的性能从 ~15 秒提高到 ~1.5 秒 - 非常感谢 :)
  • 索引应该会改善这一点。同样,删除其中一个子查询的日期格式部分可能很简单,允许在 timstampColumn 字段上使用索引(如有必要,您可以将时间添加到 28 天前的计算中,从而允许查询直接使用 timstampColumn 上的索引)
【解决方案2】:

(看看SHOW CREATE TABLE可能会有所帮助。)

AND  DATE_FORMAT(timstampColumn, '%Y/%m/%d') > DATE_ADD(CURRENT_DATE,
            INTERVAL - 28 DAY))

不能使用索引;这可能是等价的:

AND  timstampColumn > CURRENT_DATE - INTERVAL 28 DAY

请提供EXPLAIN

你用的是什么版本?

IN ( SELECT ... ) 子句转换为“派生”表可能会有所帮助(取决于版本):

JOIN ( SELECT ... ) ON ...

WHERE (x,y) IN ... 没有得到很好的优化。它们是什么类型的值?

使用 *_ci 排序规则,

UPPER(LEFT(table1.cloumn1, 1)) IN ('A', 'B')

可以做到:

LEFT(table1.cloumn1, 1) IN ('A', 'B')

这不会显着提高性能。最好不要为了测试而拆开列。

这可能使用涉及cloumn1的索引:

    table1.cloumn1 >= 'A'
AND table1.cloumn1 <  'C'

AND'd 在一起的事情的顺序很少重要。 INDEX 中的顺序会产生很大的不同。

【讨论】:

    猜你喜欢
    • 2014-01-28
    • 1970-01-01
    • 2019-07-09
    • 2013-06-17
    • 2017-02-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-11
    相关资源
    最近更新 更多