使用 shedlock 在预定时间清除 spring 批处理作业表的旧记录时出现死锁

Deadlock while purging old records of spring batch job tables at scheduled time using shedlock

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

我看到这个错误。

Transaction (Process ID 293) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.; nested exception is com.microsoft.sqlserver.jdbc.SQLServerException: Transaction (Process ID 293) was deadlocked on lock resources with another process and has been chosen as the deadlock victim.

我的批量更新查询:

 jdbcTemplate.batchUpdate(purgeQueryArray)

我有 7-8 个 table 超过 7 天的数据需要清除。现在,在较低的环境中,由于数据量较小,因此可以正常工作。在生产中,我们每个 table 中有多达 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)

    }

Shedlocktable数据:

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);

提前致谢

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

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

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

带有 CAST(LAST_UPDATED as date) < DATEADD(day, -7, GETDATE())) 的 WHERE 子句覆盖该列,即使有指数。你能直接比较 LAST_UPDATED 和完全相同类型的局部变量吗?

也许检查执行计划。这可能表示有问题。

另一种选择是先获取 ID,前提是它们足够 table。然后在单独的事务中使用 table 和要删除的数据以及临时 table 进行删除。我会分批循环删除。

如果 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

有趣的东西。祝你好运。