我知道这个问题已被问了一百次,但不幸的是,这些答案都没有帮助,因为大多数都是多年前的事,它应该证明另一个问题的合理性。
我有两个表,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 = ",$1,";" }' | mysql database;
now=$(date +"%Y/%m/%d %T")
echo "[ $now ] $i.000"
done
这可以在开始时处理每秒一千条记录,但在几个循环之后它每10-20秒就会下降到一千条记录。由于我需要清除100k行,这需要将近一个月的时间才能完成,这与清除记录的时间差大致相同,因此它永远不会完成(特别是因为有多个数据库需要这样做)。
数据库存储在SSD Crucial_CT500MX200SSD1
上,软件MariaDB 10.1
存储在InnoDB
引擎中。 innodb_flush_log_at_trx_commit
设置为0
以避免不必要的磁盘使用。
根据atop
,瓶颈是磁盘,CPU几乎处于休眠状态,大部分内存都在系统缓存中。
结构转储:https://gist.github.com/Slind14/0da34e09dba91cf411db2ead5ad666ef
答案 0 :(得分:1)
请参阅http://mysql.rjweb.org/doc.php/deletebig
讨论了从大表中有效删除大量行的各种方法。
可能效果最好的是基于PRIMARY KEY
(它有这样的?)遍历表格,删除该块中符合条件的行。
这是有效的原因是它一次看100行,与它们一起工作,然后继续前进到接下来的100行。相反,使用LIMIT
和无界WHERE
子句可能需要一遍又一遍地扫描相同的行。
您提到了JOIN
。根据成本的高低,100可能是一个很好的限制。如果JOIN
不是太繁重,那么1000就可以正常工作。
如果这是一项重复性任务,请考虑使用PARTITIONing
,以便DROP PARTITION
可以立即使用0.100000
0.333333
0.003333
。这件事。