从巨大的 MySQL innoDB Table 中删除记录

Deleting Records from huge MySQL innoDB Table

我知道这个问题已被问过一百次,但不幸的是 none 这些答案有所帮助,因为大多数都已经很多年了,应该证明另一个答案是正确的。

我有两个表,records+140kk rows/+24GB 以及 extra+89kk rows/+70GB

每个 extra 行与 records 有一个外键关系,在两个表之间正确设置了索引。 records 中的删除将删除级联到相关的 extra 行。

我需要在制作过程中清除旧记录。 运行 DELETE FROM records WHERE WHERE created < UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL 30 DAY)) ORDER BY id LIMIT 1000; 需要无穷无尽(我在 20 分钟后它还在 init 的地方杀了它)。对 SELECT 执行相同操作仅需几毫秒即可完成。

因为 SELECT 太快了,我用 id IN (SELECT id subquery^) 试了一下,不幸的是这并没有改变什么,所以我在 15 分钟后就把它杀了。

由于删除单个记录的速度很快,我最终采用了以下解决方法:

for i in `seq 1 100000`; do
    mysql database -e "SELECT id FROM records WHERE created < UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL 30 DAY)) ORDER BY id LIMIT 1000;" | sed 's;/|;;g' | awk '{if(NR>1)print "DELETE FROM records WHERE id = ",,";" }' | mysql database;
    now=$(date +"%Y/%m/%d %T")
    echo "[ $now ] $i.000"
done

开始时每秒可以处理一千条记录,但经过几次循环后,它会下降到每 10-20 秒一千条。由于我需要清除 10 万行,这将需要将近一个月的时间才能完成,这与要清除的记录的时间范围大致相同,因此它永远不会完成(特别是因为有多个数据库需要它)。

数据库存储在 SSD Crucial_CT500MX200SSD1 上,软件 MariaDB 10.1 带有 InnoDB 引擎。 innodb_flush_log_at_trx_commit 设置为 0 以避免不必要的磁盘使用。

根据 atop 瓶颈是磁盘,CPU 几乎处于休眠状态,大部分内存在系统缓存中。

结构转储:https://gist.github.com/Slind14/0da34e09dba91cf411db2ead5ad666ef

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

它讨论了从大 tables 中有效删除大量行的各种方法。

可能最有效的方法是根据 PRIMARY KEY(它有这样的?)遍历 table,删除该块中符合条件的任何行。

之所以高效,是因为它一次查看 100 行,处理它们,然后才继续处理下 100 行。相反,使用 LIMIT 和无限 WHERE 子句可能需要一遍又一遍地扫描相同的行。

您提到了 JOIN。根据成本的高低,100 可能是一个很好的限制。如果JOIN不是太繁琐的话,1000或许也行。

如果这是一项重复性任务,请考虑使用 PARTITIONing,以便 DROP PARTITION 可用于 'instantly' 执行任务。