数据归档是高负载OLTP系统中最重要的任务之一,这些系统处理着大型表。历史上,最初的实现尝试非常简单:数据通过脚本或应用程序手动删除或复制到单独的表中。后来,出现了更系统化的方法,考虑了事务完整性和对数据库主要工作的最小影响。
这里的问题不仅在于信息的物理迁移,还在于保持一致性、最小化阻塞和确保高性能。归档过程中的错误可能会导致数据丢失、用户被锁定或负载显著增加。
解决方案是使用带事务控制的批量操作,以及生成具有相同结构的特殊归档表,或者通过调度程序和过程进行自动化。
代码示例:
-- 将超过一年的5000条记录迁移到归档表中 INSERT INTO archive_orders SELECT * FROM orders WHERE order_date < DATEADD(year, -1, GETDATE()) AND id IN (SELECT TOP 5000 id FROM orders WHERE order_date < DATEADD(year, -1, GETDATE()) ORDER BY id); DELETE FROM orders WHERE id IN (SELECT TOP 5000 id FROM orders WHERE order_date < DATEADD(year, -1, GETDATE()) ORDER BY id);
关键特点:
在大规模DELETE旧记录时有什么风险,如何避免?
大规模DELETE可能导致锁升级和整个数据库性能下降。通过在循环中以小批量进行删除,或使用LIMIT/TOP(如果数据库支持)可以避免这一情况。
WHILE 1=1 BEGIN DELETE TOP (1000) FROM orders WHERE order_date < '2023-01-01'; IF @@ROWCOUNT = 0 BREAK; END
可以使用TRUNCATE删除归档数据吗?
TRUNCATE删除表中的所有行,不适合对特定行进行条件清除。它不会触发触发器,不支持WHERE,只能用于完全清除,而不是选择性归档。
如何确保迁移正确完成,如果删除和插入是在不同的事务中进行?
最好在一个事务中进行行迁移:首先将数据复制到归档中,然后从主表中删除。否则,在操作之间发生故障时可能会导致不一致性。
BEGIN TRANSACTION INSERT INTO archive_orders SELECT * FROM orders WHERE ... DELETE FROM orders WHERE ... COMMIT
工程师在工作时间运行一个处理一百万条记录的脚本 DELETE FROM logs WHERE event_date < '2022-01-01'。
优点:
缺点:
计划在夜间通过存储过程和调度任务每次迁移5000行,并记录每批的成功情况。
优点:
缺点: