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_categories 有 97K 条记录,如 MASTER 有 40K 条记录。我们在 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
。
- (底线:你没有说你是什么版本 运行;我无法预测哪种表述最好。)
- 我的博客避免了公式化辩论。
任何人都可以帮助我重新编写查询以加快执行时间吗?执行需要 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_categories 有 97K 条记录,如 MASTER 有 40K 条记录。我们在 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
.
我在 http://mysql.rjweb.org/doc.php/deletebig
中讨论了这两种技术以及其他技术至于查询公式:
- 它是版本相关的; MySQL 的旧版本在某些口味上表现不佳。
NOT IN (SELECT ...)
和NOT EXISTS
往往表现最差。IN (SELECT ...)
and/orEXISTS
可能更好。- "Multi-table
DELETE
是另一种选择。它的工作方式类似于JOIN
。 - (底线:你没有说你是什么版本 运行;我无法预测哪种表述最好。)
- 我的博客避免了公式化辩论。