以滚动方式从MySQL中删除旧行的最佳方法是什么?

时间:2012-02-27 20:58:47

标签: mysql

我发现自己想要在很多应用程序中滚动删除早于(x)-days的行。在高流量桌上最有效地做到这一点的最佳方法是什么?

例如,如果我有一个存储通知的表,我只想保留7天。或高分我只想保持31天。

现在我保留一行存储发布的纪元时间,然后运行一个每小时运行一次的cron作业,并以这样的增量删除它们:

DELETE FROM my_table WHERE time_stored < 1234567890 LIMIT 100

我这样做直到mysql_affected_rows返回0。

我曾经一次完成所有操作,但是当INSERTS堆积起来时,应用程序中的所有内容都会挂起30秒左右。添加LIMIT可以缓解这种情况,但我想知道是否有更好的方法可以做到这一点。

6 个答案:

答案 0 :(得分:57)

尝试创建将在您想要的时间间隔后自动在数据库上运行的事件。

这是一个例子: 如果要从某个表'tableName'中删除超过30天的条目,请使用列条目'datetime'。然后每天执行以下查询,这将执行所需的清理操作。

CREATE EVENT AutoDeleteOldNotifications
ON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL 1 DAY 
ON COMPLETION PRESERVE
DO 
DELETE LOW_PRIORITY FROM databaseName.tableName WHERE datetime < DATE_SUB(NOW(), INTERVAL 30 DAY)

我们需要添加ON COMPLETION PRESERVE以在每次运行后保留事件。您可以在此处找到更多信息:http://www.mysqltutorial.org/mysql-triggers/working-mysql-scheduled-event/

答案 1 :(得分:28)

结帐MySQL Partitioning

  

通过删除仅包含该数据的分区(或多个分区),通常可以轻松地从分区表中删除失去其实用性的数据。相反,在某些情况下,通过添加一个或多个新分区来特别存储该数据,可以极大地促进添加新数据的过程。

参见例如本节将介绍如何应用它:

  

MySQL Partition Pruning

这一个:

  

Partitioning by dates: the quick how-to

答案 2 :(得分:2)

不是单独对表执行删除,而是先尝试收集匹配的键,然后再执行DELETE JOIN

鉴于上面的示例查询

DELETE FROM my_table WHERE time_stored < 1234567890 LIMIT 100 ;

您可以将LIMIT排除在外。

假设您要删除超过31天的数据。

让我们以秒计算31天(86400 X 31 = 2678400)

  • 从密钥收集开始
  • 接下来,索引键
  • 然后,执行DELETE JOIN
  • 最后,删除收集的密钥

这是算法

CREATE TABLE delete_keys SELECT id FROM my_table WHERE 1=2;
INSERT INTO delete_keys
SELECT id FROM
(
    SELECT id FROM my_table
    WHERE time_stored < (UNIX_TIMESTAMP() - 2678400)
    ORDER BY time_stored
) A LIMIT 100;
ALTER TABLE delete_keys ADD PRIMARY KEY (id);
DELETE B.* FROM delete_keys
INNER JOIN my_table B USING (id);
DROP TABLE delete_keys;

如果密钥收集时间少于5分钟,则每隔5分钟运行一次此查询。

试一试!!!

更新2012-02-27 16:55美国东部时间

这是应该加快关键收集的一些事情。添加以下索引:

ALTER TABLE my_table ADD INDEX time_stored_id_ndx (time_stored,id);

这将更好地支持填充delete_keys表的子查询,因为这提供了覆盖索引,以便仅从索引中检索字段。

更新2012-02-27 16:59 EDT

由于您必须经常删除,您可能希望每两个月尝试一次

OPTIMIZE TABLE my_table;

这会在所有那些令人讨厌的小删除每5分钟两个月后对表进行碎片整理

答案 3 :(得分:1)

在我的公司,我们也有类似的情况。我们有一个包含过期密钥的表。我们有一个cron来清理它:

DELETE FROM t1 WHERE expiration < UNIXTIME(NOW());

每小时运行一次,但我们遇到的问题与您遇到的问题类似。我们将它增加到每分钟一次。然后每分钟6次。使用基本上执行查询的bash脚本设置cron,然后休眠几秒钟并重复直到分钟结束。

增加的频率显着减少了我们删除的行数。这缓解了争论。这是我要去的路线。

但是,如果您发现仍有太多行要删除,请使用限制并在它们之间进行休眠。例如,如果要删除50k行,请在它们之间进行2秒睡眠的10k块。这将有助于查询堆叠,并允许服务器在这些批量删除之间执行一些正常操作。

答案 4 :(得分:1)

您可能需要考虑在设计中引入master/slave (replication)解决方案。如果将所有读取流量转移到从站,则打开主站以处理“即时”CRUD活动,然后将其复制到从站(您的读取服务器)。

由于您要删除这么多记录,因此您可能需要考虑在删除行的表上运行optimize

答案 5 :(得分:0)

最终使用它仅保留最后100行,因此在频繁执行(每分钟)时会有很大的滞后时间

delete a from tbl a left join (
    select ID
    from tbl
    order by id desc limit 100
) b on a.ID = b.ID
where b.ID is null;