【问题标题】:MySQL poor OR and ISNULL performanceMySQL 较差的 OR 和 ISNULL 性能
【发布时间】:2016-10-02 14:09:34
【问题描述】:

我对一些奇怪的 mysql 性能行为感到非常惊讶。我的以下查询需要大约 3 个小时才能运行:

UPDATE ips_invoice AS f SET ips_locality_id = (
        SELECT ips_locality_id 
        FROM ips_user_unit_locality AS uul 
        JOIN ips_user AS u ON u.id = uul.ips_user_id 
        WHERE 
            (u.id = f.ips_user_id OR u.ips_user_id_holder = f.ips_user_id) AND 
            uul.date <= f.date 

        ORDER BY `date` DESC 
        LIMIT 1 
) 
WHERE f.ips_locality_id IS NULL;

我也尝试了以下一种,但得到了相同的性能结果:

UPDATE ips_invoice AS f SET ips_locality_id = (
        SELECT ips_locality_id 
        FROM ips_user_unit_locality AS uul 
        JOIN ips_user AS u ON u.id = uul.ips_user_id 
        WHERE 
            IFNULL(u.ips_user_id_holder, u.id) = f.ips_user_id 
            AND 
            uul.date <= f.date 

        ORDER BY `date` DESC 
        LIMIT 1 
) 
WHERE f.ips_locality_id IS NULL;

逻辑是:如果“ips_user_id_holder”列不为空,我应该使用它,如果不是,我应该使用“id”列。

如果我将查询拆分为两个查询,每个查询需要 15 秒才能运行:

     UPDATE ips_invoice AS f SET ips_locality_id = (
                SELECT ips_locality_id 
                FROM ips_user_unit_locality AS uul 
                JOIN ips_user AS u ON u.id = uul.ips_user_id 
                WHERE 
                    u.ips_user_id_holder = f.ips_user_id 
                    AND 
                    uul.date <= f.date 

                ORDER BY `date` DESC 
                LIMIT 1 
        ) 
        WHERE f.ips_locality_id IS NULL;

UPDATE ips_invoice AS f SET ips_locality_id = (
                SELECT ips_locality_id 
                FROM ips_user_unit_locality AS uul 
                JOIN ips_user AS u ON u.id = uul.ips_user_id 
                WHERE 
                    u.id = f.ips_user_id 
                    AND 
                    uul.date <= f.date 

                ORDER BY `date` DESC 
                LIMIT 1 
        ) 
        WHERE f.ips_locality_id IS NULL;

这不是我第一次遇到 Mysql “OR” 或 “null checks” 的问题 相对简单的查询 (Why this mysql query (with is null check) is so slower than this other one?)。

ips_invoice 表有大约 400.000 条记录,ips_user_unit_locality 大约有 100.000 条记录,ips_user 大约有 35.000 条记录。

我在 Ubuntu Amazon EC2 实例中运行 MySQL 5.5.49。

那么,第一个和第二个查询有什么问题?造成显着性能差异的原因是什么?

【问题讨论】:

    标签: mysql sql performance


    【解决方案1】:

    第一个和第二个查询没有任何“错误”。但是,当您在join 条件(或等效的相关子查询条件)中使用or 时,引擎通常无法使用索引。

    这让一切都变得非常缓慢。

    您似乎至少了解一种解决方法,所以我不会提出任何其他建议。

    编辑:

    我会注意到您的查询并不完全符合您在文本中指定的内容。它获取两个用户 ID 中的任何一个的最新日期。您似乎想优先考虑 id。如果是这样,这就是您想要的查询:

    UPDATE ips_invoice f
        SET ips_locality_id =
            COALESCE( (SELECT ips_locality_id 
                       FROM ips_user_unit_locality uul JOIN
                            ips_user u
                            ON u.id = uul.ips_user_id 
                       WHERE u.ips_user_id_holder, f.ips_user_id AND
                             uul.date <= f.date 
                       ORDER BY uul.date DESC
                       LIMIT 1
                      ),
                      (SELECT ips_locality_id 
                       FROM ips_user_unit_locality uul
                       WHERE uul.ips_user_id = f.ips_user_id AND
                             uul.date <= f.date 
                       ORDER BY uul.date DESC
                       LIMIT 1
                      )
                    )
    WHERE f.ips_locality_id IS NULL;
    

    【讨论】:

    • 哇,有趣的替代方式。非常感谢!
    【解决方案2】:
    1. 使用多表UPDATE而不是= ( SELECT ...)

    2. 写两个独立的UPDATEs,而不是OR

    【讨论】:

      猜你喜欢
      • 2018-11-17
      • 2014-06-30
      • 2010-10-21
      • 1970-01-01
      • 1970-01-01
      • 2012-04-14
      • 1970-01-01
      • 2017-03-14
      相关资源
      最近更新 更多