以下是我要解决的问题:我最近完成了数据层重新设计,允许我跨多个分片对数据库进行负载均衡。为了保持碎片平衡,我需要能够将数据从一个碎片迁移到另一个碎片,这涉及从碎片A复制到碎片B,然后从碎片A中删除记录。但是我有几个非常大的表,并且有许多外键指向它们,因此从表中删除单个记录可能需要一秒钟以上。
在某些情况下,我需要从表中删除数百万条记录,实际上只需要很长时间。
禁用外键不是一种选择。删除大批量的行也不是一种选择,因为这是一个生产应用程序,而大型删除会锁定太多资源,从而导致失败。我正在使用Sql Server,而且我知道分区表,但是对分区的限制(以及企业版的许可费用)是不切实际的,以至于它们是不可能的。
当我开始研究这个问题时,我认为困难的部分是编写算法,该算法将如何从叶级别删除行直到数据模型的顶部,这样就不会违反任何外键约束。办法。但解决这个问题对我没有好处,因为删除需要在一夜之间消失的记录需要数周时间。
我已经建立了将数据标记为虚拟删除的方式,因此就应用程序而言,数据已经消失,但我仍在处理大型数据文件,大型备份和较慢的查询,因为桌子的大小。
有什么想法吗?我已经在这里阅读了较旧的相关帖子,但没有发现任何有用的信息。
答案 0 :(得分:27)
请参阅:Optimizing Delete on SQL Server
此MS支持文章可能很有用:How to resolve blocking problems that are caused by lock escalation in SQL Server:
将大批量操作分解为几个较小的操作。对于 例如,假设您运行以下命令 查询删除几百个 审计中的一千条旧记录 表,然后你发现它 导致锁定升级被阻止 其他用户:
DELETE FROM LogMessages WHERE LogDate < '2/1/2002'
删除这些记录 一次一百,你可以 大大减少了数量 每个事务累积的锁 并防止锁升级。对于 例如:
SET ROWCOUNT 500 delete_more: DELETE FROM LogMessages WHERE LogDate < '2/1/2002' IF @@ROWCOUNT > 0 GOTO delete_more SET ROWCOUNT 0
通过提高查询效率来减少查询的锁占用空间 可能。大扫描或大扫描 书签查询的数量可以 增加锁定的机会 升级;另外,它增加了 死锁的可能性,一般而言 对并发性产生负面影响 性能
答案 1 :(得分:16)
delete_more:
DELETE TOP(500) FROM LogMessages WHERE LogDate < '2/1/2002'
IF @@ROWCOUNT > 0 GOTO delete_more
您可以使用Mitch建议的SET ROWCOUNT
获得相同的结果,但DELETE
{{1}}以及SQL Server未来版本中的其他一些操作将不支持3}:
使用SET ROWCOUNT不会影响DELETE,INSERT和UPDATE SQL Server未来版本中的语句。避免使用SET ROWCOUNT 在新的开发工作中使用DELETE,INSERT和UPDATE语句, 并计划修改当前使用它的应用程序。对于类似的 行为,使用TOP语法。有关更多信息,请参阅TOP (处理SQL)。
答案 2 :(得分:1)
您可以创建新文件,复制除“已删除”行之外的所有文件,然后交换表中的名称。最后,放下旧桌子。如果您删除了大部分记录,那么这实际上可能会更快。
答案 3 :(得分:1)
另一个建议是重命名表并添加状态列。当status = 1(已删除)时,您不希望它显示。然后,您创建一个与原始表同名的视图,当状态为null或= 0时,它将从表中进行选择(具体取决于您实现它的方式)。删除对用户来说是立竿见影的,并且后台作业可以每十五分钟运行一次,删除除了dbas之外没有其他任何人运行的记录。
答案 4 :(得分:0)
如果您使用的是SQL 2005或2008,那么使用“快照隔离”可能会对您有所帮助。它允许数据在进行基础数据更新操作处理时保持对用户可见,然后在数据提交后立即显示数据。即使您删除需要30分钟才能运行,您的应用程序将在此期间保持在线状态。
以下是快照锁定的快速入门:
http://www.mssqltips.com/tip.asp?tip=1081
虽然你仍然应该尽快加快删除速度,但这可以减轻一些负担。
答案 5 :(得分:0)
您可以使用while循环删除小批量,如下所示:
DELETE TOP (10000) FROM LogMessages WHERE LogDate < '2/1/2002'
WHILE @@ROWCOUNT > 0
BEGIN
DELETE TOP (10000) FROM LogMessages WHERE LogDate < '2/1/2002'
END
答案 6 :(得分:0)
如果该表的相当大的百分比要符合删除条件(接近或超过50%),则创建一个临时表并包含将不删除的记录是“便宜”的做法(反转WHERE条件),然后截断原始表,然后使用要保留的记录重新填充该表。
DELETE FROM TABLE WHERE ROW_TO_DELETE = 'OK';
GO
-->
INSERT INTO #TABLE WHERE NOT ROW_TO_DELETE = 'OK';
TRUNCATE TABLE;
INSERT INTO TABLE (SELECT * FROM #TABLE);
GO
答案 7 :(得分:-1)
这是解决问题的方法。
DECLARE @RC AS INT
SET @RC = -1
WHILE @RC <> 0
BEGIN
DELETE TOP(1000000) FROM [Archive_CBO_ODS].[CBO].[AckItem] WHERE [AckItemId] >= 300
SET @RC = @@ROWCOUNT
--SET @RC = 0
END