我正在测试一次删除很多记录的过程。不能TRUNCATE TABLE
,因为其中存在需要保留的记录。
由于数量巨大,我将删除操作分成了类似于以下的循环:
-- Do not block if records are locked.
SET LOCK_TIMEOUT 0
-- This process should be chosen as a deadlock victim in the case of a deadlock.
SET DEADLOCK_PRIORITY LOW
SET NOCOUNT ON
DECLARE @Count
SET @Count = 1
WHILE @Count > 0
BEGIN TRY
BEGIN TRANSACTION -- added per comment below
DELETE TOP (1000) FROM MyTable WITH (ROWLOCK, READPAST) WHERE MyField = SomeValue
SET @Count == @@ROWCOUNT
COMMIT
END TRY
BEGIN CATCH
exec sp_lock -- added to display the open locks after the timeout
exec sp_who2 -- shows the active processes
IF @@TRANCOUNT > 0
ROLLBACK
RETURN -- ignoring this error for brevity
END CATCH
MyTable是一个群集表。MyField在聚集索引的第一列中。它表示记录的逻辑分组,因此MyField = SomeValue
经常选择许多记录。我不在乎删除它们的顺序,只要一次处理一组即可。该表上没有其他索引。
我添加了ROWLOCK
提示以避免尝试在生产中看到锁升级。我添加了READPAST
提示,以避免删除其他进程锁定的记录。那永远不会发生,但我正在努力确保安全。
问题:有时,当唯一运行该循环时,该循环会遇到锁定超时1222“超出了锁定请求超时时间”。
我确信在测试此过程时,该系统上没有其他活动,因为它是我自己的开发人员箱,没有其他人连接,没有其他进程在运行,并且探查器未显示任何活动。
一秒钟后,我可以重新运行同一脚本,它从中断处开始取乐,愉快地删除记录-直到下一个锁定超时为止。
我试过BEGIN TRY
/BEGIN CATCH
来忽略1222错误,然后重试删除,但是由于相同的锁定超时错误,它立即再次失败。如果在重试之前添加了短暂的延迟,它也会再次失败。
我认为锁定超时是由于诸如页面拆分之类的原因,但是我不确定为什么这会与当前循环迭代冲突。先前的delete语句应该已经完成,并且我认为这意味着所有页面拆分也已完成。
为什么DELETE循环对自身造成锁定超时?
该过程是否有办法避免此锁定超时或检测到可以安全恢复?
这是在SQL Server 2005上。
- 编辑 -
我将Lock:Timeout事件添加到了探查器。在删除期间,它在PAGELOCK上超时:
Event Class: Lock:Timeout
TextData: 1:15634 (one example of several)
Mode: 7 - IU
Type: 6 - PAGE
DBCC PAGE报告这些页面不在主数据库(ID 1)的范围内。
-编辑2-
我加了一个BEGIN TRY
/BEGIN CATCH
并exec sp_lock
在catch块中运行了一个。这是我所看到的:
spid dbid ObjId IndId Type Resource Mode Status
19 2 1401108082 1 PAG 1:52841 X GRANT (tempdb.dbo.MyTable)
19 2 1401108082 0 TAB IX GRANT (tempdb.dbo.MyTable)
Me 2 1401108082 0 TAB IX GRANT (tempdb.dbo.MyTable)
Me 1 1115151018 0 TAB IS GRANT (master..spt_values) (?)
SPID 19是一个SQL Server任务管理器。这些任务管理器之一为什么要获取MyTable的锁?
我找到了答案:我的循环删除与ghost清除proc冲突。
根据尼古拉斯的建议,我添加了BEGIN TRANSACTION
和COMMIT
。我将删除循环包装在BEGIN TRY
/中BEGIN CATCH
。在BEGIN CATCH
之前,权ROLLBACK
,我跑sp_lock
和sp_who2
。(我在上面的问题中添加了代码更改。)
当我的进程被阻塞时,我看到以下输出:
spid dbid ObjId IndId Type Resource Mode Status
------ ------ ----------- ------ ---- -------------------------------- -------- ------
20 2 1401108082 0 TAB IX GRANT
20 2 1401108082 1 PAG 1:102368 X GRANT
SPID Status Login HostName BlkBy DBName Command CPUTime DiskIO
---- ---------- ----- -------- ----- ------ ------------- ------- ------
20 BACKGROUND sa . . tempdb GHOST CLEANUP 31 0
为了将来参考,当SQL
Server删除记录时,它将对它们进行一些设置以仅将它们标记为“幽灵记录”。每隔几分钟,就会运行一个称为“幽灵清理”的内部过程来回收已被完全删除的记录页面(即,所有记录都是幽灵记录)。
这个问题在ServerFault上讨论了鬼清除过程。
这是Paul S. Randal对鬼清除过程的解释。
可以使用跟踪标志来禁用重影清理过程。
但是在这种情况下,我不必这样做。
我最终添加了100毫秒的锁定等待超时。这会导致在虚影记录清理过程中偶尔出现锁定等待超时,但这是可以接受的。我还添加了一个我们的循环,该循环最多可重试5次锁定超时。有了这两个更改,我的过程现在通常可以完成。现在,只有在有一个非常长的进程将大量数据推入到表或页面锁的过程中,该进程需要清除我的进程需要清除的数据时,它才会超时。
编辑2016-07-20
最终代码如下所示:
-- Do not block long if records are locked.
SET LOCK_TIMEOUT 100
-- This process volunteers to be a deadlock victim in the case of a deadlock.
SET DEADLOCK_PRIORITY LOW
DECLARE @Error BIT
SET @Error = 0
DECLARE @ErrMsg VARCHAR(1000)
DECLARE @DeletedCount INT
SELECT @DeletedCount = 0
DECLARE @LockTimeoutCount INT
SET @LockTimeoutCount = 0
DECLARE @ContinueDeleting BIT,
@LastDeleteSuccessful BIT
SET @ContinueDeleting = 1
SET @LastDeleteSuccessful = 1
WHILE @ContinueDeleting = 1
BEGIN
DECLARE @RowCount INT
SET @RowCount = 0
BEGIN TRY
BEGIN TRANSACTION
-- The READPAST below attempts to skip over locked records.
-- However, it might still cause a lock wait error (1222) if a page or index is locked, because the delete has to modify indexes.
-- The threshold for row lock escalation to table locks is around 5,000 records,
-- so keep the deleted number smaller than this limit in case we are deleting a large chunk of data.
-- Table name, field, and value are all set dynamically in the actual script.
SET @SQL = N'DELETE TOP (1000) MyTable WITH(ROWLOCK, READPAST) WHERE MyField = SomeValue'
EXEC sp_executesql @SQL, N'@ProcGuid uniqueidentifier', @ProcGUID
SET @RowCount = @@ROWCOUNT
COMMIT
SET @LastDeleteSuccessful = 1
SET @DeletedCount = @DeletedCount + @RowCount
IF @RowCount = 0
BEGIN
SET @ContinueDeleting = 0
END
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0
ROLLBACK
IF Error_Number() = 1222 -- Lock timeout
BEGIN
IF @LastDeleteSuccessful = 1
BEGIN
-- If we hit a lock timeout, and we had already deleted something successfully, try again.
SET @LastDeleteSuccessful = 0
END
ELSE
BEGIN
-- The last delete failed, too. Give up for now. The job will run again shortly.
SET @ContinueDeleting = 0
END
END
ELSE -- On anything other than a lock timeout, report an error.
BEGIN
SET @ErrMsg = 'An error occurred cleaning up data. Table: MyTable Column: MyColumn Value: SomeValue. Message: ' + ERROR_MESSAGE() + ' Error Number: ' + CONVERT(VARCHAR(20), ERROR_NUMBER()) + ' Line: ' + CONVERT(VARCHAR(20), ERROR_LINE())
PRINT @ErrMsg -- this error message will be included in the SQL Server job history
SET @Error = 1
SET @ContinueDeleting = 0
END
END CATCH
END
IF @Error <> 0
RAISERROR('Not all data could be cleaned up. See previous messages.', 16, 1)
问题内容: 我希望我的for循环不应该一次执行,而要在每次迭代后等待超时。例如: 我发现了很多关于堆栈溢出的解决方案,例如: 但是在所有实现中,循环最初等待3000毫秒,然后立即执行整个循环。有没有一种方法可以等待1000毫秒后调用每次迭代。 问题答案: 您可以使用简单的数学方法来解决: 1000ms:0 4000ms:1 7000ms:2 10000ms:3 13000ms:4 … 跟随评论 您
问题内容: 更新记录时,我反复出现锁定超时超出异常的情况。 我正在使用Java Struts 2.1 Hibernate配置。使用的数据库是MYSQL。 任何人都知道如何解决它。 问题答案: 这里有一些建议: “ 锁定等待超时 ”通常发生在事务正在等待要更新的数据行上,而该行已被某些其他事务锁定时。 在大多数情况下,问题出在数据库方面。可能的原因可能是表格设计不当,数据量大,约束等。 请查看这个详
问题内容: 我正在使用Hibernate,试图模拟2个并发更新到数据库中的同一行。 编辑:我将em1.getTransaction()。commit移到em1.flush()之后;我没有收到任何StaleObjectException,两个事务已成功提交。 我在上遇到以下异常。为什么? 问题答案: 好吧,您正试图陷入僵局,并且成功了:-) Transaction1开始,与您的实体更新(和锁定)行。
您可以在下面的问题中看到:(liquibase-lock-reasons)当Liquibase操作中断时客户端Liquibase锁定,使Liquibase处于锁定状态。 我想知道是否有一种方法可以配置Liquibase,使其从列中的日期和时间自动检测这种情况。我想如果你已经拿着锁一个小时了--你应该把它叫做过期锁。
专家。Javascript没有产生期望的延迟效果<从其他问题开始,我知道,问题在于settimeout和我使用它的方式。但我仍然无法理解Settimeout是如何工作的。所以我把代码放在这里。出于知识目的,只需要使用Javascript 事实上,我正试图用javascript来澄清我关于这个闭包的概念。它们是Javascript中扭曲的东西吗?
我在解决如何正确处理JSF中会话的自动销毁方面遇到了困难。当然,此时,容器会使会话失效,从而也会在会话范围的bean上调用@PreDestroy方法。 在某些会话范围bean的预销毁中,我们正在注销一些侦听器,如下所示: 然而,getWS()方法实际上试图获取对另一个会话范围bean的引用,但失败了,如返回null。Ryan Lubke认为,后者似乎是正常的JSF行为: 我们遵守这里的规范。我不确