2010-04-30 71 views
1

这里是我的场景:我们有一个数据库,我们称它为Logging,并带有一个表格,该表格包含来自Log4Net(通过MSMQ)的记录。数据库的恢复模式设置为简单:我们不关心事务日志 - 它们可以翻转。Sql Server:分块删除仍填满事务日志;失败时,所有删除都回滚 - 为什么?

我们有一个工作,它使用来自sp_spaceused的数据来确定我们是否达到了一定的大小阈值。如果超过阈值,我们确定需要删除多少行才能将大小降至该阈值的百分之x。 (顺便说一句,我使用exec sp_spaceused MyLogTable,TRUE来获得行数和他们的平均大小的粗略近似值,虽然我不相信这是最好的方式去做,但这是一个不同的问题。 )

然后我尝试块删除(比如说,5000在时间)通过循环到一个存储过程,基本上做了这样的电话:

DELETE TOP (@RowsToDelete) FROM [dbo].[MyLogTable] 

,直到我已经删除了需要删除的内容。

问题出在这里:如果我有很多要删除的行,则事务日志文件填满。我可以看它通过运行增长

dbcc sqlperf (logspace) 

令我困惑的是,当作业失败时,所有删除的行都会回滚。换句话说,看起来所有的块都被包装在一个隐含的事务中(以某种方式)。

我已经试过明确设置隐式事务,将每个DELETE语句包装在BEGIN和COMMIT TRAN中,但无济于事:要么所有删除的块都成功,要么根本没有。

我知道一个简单的答案是,让您的日志文件足够大,以处理尽可能多的记录,但您仍然可以将其视为单个事务处理?

对不起,如果我错过了一件容易的事情,但我已经看过很多关于日志文件增长,恢复模式等的帖子,我无法弄清楚这一点。

还有一件事:一旦作业失败,日志文件会在回落之前保持在95-100%的满量程。但是,如果我运行

checkpoint 
dbcc dropcleanbuffers 

它会直接回落到约5%的利用率。

TIA。

回答

0

简单恢复模型中的日志文件会在每个检查点自动截断。您可以像在循环结束时一样手动调用检查点,但您也可以在每次迭代中执行此操作。检查点的频率默认由SQL Server根据恢复间隔设置自动确定。

只要'所有删除都回滚',我不会看到其他解释,而是一个外部事务。你能发布清理日志的完整代码吗?你如何调用这段代码? 您对隐式交易的设置是什么?

嗯..如果日志增长并且不会自动截断,它也可能表明有一个事务正在循环之外运行。你可以在你的循环之前select @@trancount,也许每次迭代找出发生了什么?

+1

Piotr,感谢您的建议秒。事实上,我一直在执行查询分析器的“驱动程序”存储过程。关于外部事务的问题让我想知道,查询分析器默认情况下是否设置了implicit_transactions?我检查了Profilier并没有看到。但是,我将在查询分析器会话中再次尝试使用SET IMPLICIT_TRANSACTIONS OFF,并查看是否有所作为(无论全部或部分是否会在失败时回滚)。当我进行这个测试时,我会重新发布。 谢谢你,Paul – 2010-04-30 19:06:42

0

那么,我尝试了几件事情,但仍然全部删除得到回滚。我在删除之前和之后添加了printint @@TRANCOUNT,并且计数为零。然而,如果失败,所有删除操作都会回滚....我在几个地方添加了0​​(包括我在查询分析器的初始调用中,但似乎没有帮助,这是正在调用的存储过程的主体(我已@RowsToDelete 5000和8000):

SET NOCOUNT ON; 
    print N'@@TRANCOUNT PRIOR TO DELETE: ' + CAST(@@TRANCOUNT AS VARCHAR(20)); 

    set implicit_transactions off; 
    WITH RemoveRows AS 
    (
    SELECT ROW_NUMBER() OVER(ORDER BY [Date] ASC) AS RowNum 
    FROM [dbo].[Log4Net] 
) 

    DELETE FROM RemoveRows 
    WHERE RowNum < @RowsToDelete + 1 

    print N'@@TRANCOUNT AFTER DELETE: ' + CAST(@@TRANCOUNT AS VARCHAR(20)); 

正是从这个T-SQL叫做:

WHILE @RowsDeleted < @RowsToDelete 
BEGIN 
EXEC [dbo].[DeleteFromLog4Net] @RowsToDelete 
SET @RowsDeleted = @RowsDeleted + @RowsToDelete 
Set @loops = @loops + 1 
print 'Loop: ' + cast(@loops as varchar(10)) 
END 

我不得不承认,我百思不得其解我不是一个数据库大师,但我认为我足够了解这一点......

+0

嗯,我真的很讨厌承认这一点,但在@ RowsToDelete中,我通过了要删除的总行数 - 而不是我的块大小。叹。另外几张印刷声明和另一组眼睛引发了我的错误......问题解决了。 – 2010-05-03 18:37:26