0

我遇到了这个具有挑战性的场景,其中事务在执行批量更新查询时被 Microsfoft sql server 锁定。

我看到了这个错误。

事务(进程 ID 293)与另一个进程在锁资源上死锁,并已被选为死锁牺牲品。重新运行事务。; 嵌套异常是 com.microsoft.sqlserver.jdbc.SQLServerException:事务(进程 ID 293)在锁资源上与另一个进程死锁,并已被选为死锁牺牲品。

我的批量更新查询:

 jdbcTemplate.batchUpdate(purgeQueryArray)

如果超过 7 天,我有 7-8 个表需要清除其数据。现在,在较低环境的情况下,由于数据量低,所以它工作正常。在生产中,每个表中的数据多达 300k 到 500k。在删除这么多记录时,执行 spring jdbc 查询的 shedlock 任务最终陷入死锁。相同操作的 API 工作正常,但在不同时间执行,因此不确定计划任务运行时的负载。

@Scheduled(cron = "${scheduler.expression}", zone = "GMT")
    @SchedulerLock(name = "SCHEDULER_LOCK", lockAtLeastFor = "10S", lockAtMostFor = "5M")
    public void purge() {
       // prepare array of queries purgeQueryArray
       jdbcTemplate.batchUpdate(purgeQueryArray)

    }

Shedlock表数据:

SCHEDULER_LOCK 2020-10-21 00:00:15 2020-10-21 00:00:00 tomcat-406116080-2-521278230

虽然我给出了 lockAtMostFor=5M ,但看起来 lock_until 显示 15 秒,这很奇怪。这可能是原因吗?因为生产数据量需要 1-2 分钟。

任何建议将不胜感激

编辑:

DELETE FROM BATCH_STEP_EXECUTION_CONTEXT WHERE STEP_EXECUTION_ID IN (SELECT BE.STEP_EXECUTION_ID FROM BATCH_STEP_EXECUTION BE 
join BATCH_STEP_EXECUTION_CONTEXT BEC on BE.STEP_EXECUTION_ID = BEC.STEP_EXECUTION_ID
where CAST(LAST_UPDATED as date) < DATEADD(day, -7, GETDATE()));

DELETE FROM BATCH_STEP_EXECUTION WHERE JOB_EXECUTION_ID IN (SELECT JOB_EXECUTION_ID FROM BATCH_JOB_EXECUTION
where CAST(LAST_UPDATED as date) < DATEADD(day, -7, GETDATE()));

DELETE FROM BATCH_JOB_EXECUTION_CONTEXT WHERE JOB_EXECUTION_ID IN (SELECT JOB_EXECUTION_ID FROM  BATCH_JOB_EXECUTION
where CAST(LAST_UPDATED as date) < DATEADD(day, -7, GETDATE()));

DELETE FROM BATCH_JOB_EXECUTION_PARAMS WHERE JOB_EXECUTION_ID IN (SELECT JOB_EXECUTION_ID FROM BATCH_JOB_EXECUTION
where CAST(LAST_UPDATED as date) < DATEADD(day, -7, GETDATE()));

DELETE FROM BATCH_JOB_EXECUTION WHERE CAST(LAST_UPDATED as date) < DATEADD(day, -7, GETDATE());

DELETE FROM BATCH_JOB_INSTANCE WHERE JOB_INSTANCE_ID NOT IN (SELECT JOB_INSTANCE_ID FROM BATCH_JOB_EXECUTION);

提前致谢

4

1 回答 1

1

LAST_UPDATED 是否来自 BATCH_STEP_EXECUTION?(我喜欢在每一列上放一个别名,因为否则很难阅读。)

如果锁定更多或锁定时间更长,则更有可能发生死锁。优化查询很重要。如果这是不可能的,那么尝试使用小批量来最小化事务的大小。

我要做的第一件事是将数据复制到测试环境。我要尝试的第一个测试是将日期设置得足够远以排除任何记录。如果它很慢,那么它正在扫描。小批量不会有帮助 - 它可能会使情况变得更糟。

带有 CAST(LAST_UPDATED as date) < DATEADD(day, -7, GETDATE())) 的 WHERE 子句覆盖该列,即使存在索引也会导致表扫描。您可以将 LAST_UPDATED 直接与完全相同类型的局部变量进行比较吗?

也许检查执行计划。这可能表明存在问题。

如果 ID 足够稳定,另一种选择是先获取 ID。然后使用包含要删除的数据的表和可能的临时表在单独的事务中执行删除。我会分批循环删除。

如果 ID 是标识列或以其他方式单调递增,则获取要保留的最旧 ID。那应该很快。然后删除所有具有较小 ID 的内容。(确保这是有效的逻辑。)

也许避免使用 IN 子句?这是同样的结果吗?

DELETE BEC
FROM BATCH_STEP_EXECUTION_CONTEXT BEC
INNER JOIN BATCH_STEP_EXECUTION BE 
ON BE.STEP_EXECUTION_ID = BEC.STEP_EXECUTION_ID
WHERE LAST_UPDATED < @LAST_UPDATED_LIMIT -- uncover the column if possible

好玩的东西。祝你好运。

于 2020-10-21T23:48:23.270 回答