MySQL DELETE 查询的性能调整

MySQL performance tuning for DELETE query

任何人都可以帮助我重新编写查询以加快执行时间吗?执行需要 37 秒。

DELETE FROM storefront_categories 
WHERE userid IN (SELECT userid 
                FROM MASTER 
                where expirydate<'2020-2-4' 
                )

同时,这个查询只用了4.69秒就执行了。

DELETE FROM storefront_categories 
WHERE userid NOT IN (SELECT userid FROM MASTER)

table storefront_categories97K 条记录,如 MASTER40K 条记录。我们在 MASTER.expirydate 字段上创建了索引。

查询看起来很好。

我建议优化以下指标:

master(expiry_date, userid)
storefront_categories(userid)

第一个索引是covering索引master上的子查询:这意味着数据库应该能够通过查看索引来执行子查询(而索引中只有 expiry_date,它仍然需要查看 table 数据以获取相关的 userid)。

第二个索引让数据库优化 in 操作。

我会尝试 exists :

DELETE 
FROM storefront_categories 
WHERE EXISTS (SELECT 1 
              FROM MASTER M 
              WHERE M.userid = storefront_categories.userid AND
                    M.expirydate <'2020-02-04'  
              );

这里的索引应该是 metter 我希望 storefront_categories(userid) & MASTER(userid, expirydate) 上的索引。

我建议您使用具有正确索引的 NOT EXISTS

DELETE sc
    FROM storefront_categories sc
    WHERE NOT EXISTS (SELECT 1
                      FROM master m
                      WHERE m.userid = sc.userid AND
                            m.expirydate < '2020-02-04' 
                     );

您想要的索引在 master(userid, expirydate) 上。列的顺序很重要。对于这个版本,storefront_categories 上的索引没有帮助。

请注意,我更改了日期格式。我建议使用 YYYY-MM-DD 以避免歧义——并使用完整的 10 个字符。

删除 40K 行时,预计需要一些时间。主要成本(假设足够的索引和适当的查询)是 "atomic" 删除的事务语义的开销。这涉及为被删除的每一行制作一个副本,以防发生崩溃。这样,InnoDB 可以将数据库恢复到崩溃前的状态。

当删除一个 table 的 40% 时,将行复制到另一个 table 然后交换 table 会快得多。

删除大量行时(无论百分比如何),最好分块进行。而且最好在PRIMARY KEY.

的基础上走完table

我在 http://mysql.rjweb.org/doc.php/deletebig

中讨论了这两种技术以及其他技术

至于查询公式:

  • 它是版本相关的; MySQL 的旧版本在某些口味上表现不佳。
  • NOT IN (SELECT ...)NOT EXISTS 往往表现最差。
  • IN (SELECT ...) and/or EXISTS 可能更好。
  • "Multi-table DELETE 是另一种选择。它的工作方式类似于 JOIN
  • (底线:你没有说你是什么版本 运行;我无法预测哪种表述最好。)
  • 我的博客避免了公式化辩论。