【问题标题】:Database query hangs when running a select query against a table that a spring batch process is working on对 Spring 批处理正在处理的表运行选择查询时,数据库查询挂起
【发布时间】:2019-06-20 01:32:50
【问题描述】:

我有一个文件存储应用程序,它有两个服务(作业服务和文件服务)作为 Spring Boot 应用程序。当用户上传文件时,根据文件类型,它通过弹簧批处理作业进行处理,其缩略图和预览是使用 FFMpeg 和/或 ImageMagick 生成的。然后将这些数据写入数据库(目前在本地 Microsoft SQL Server 2017 Developer 上开发)。

较小的文件按预期工作,但我想通过发送一堆较大的音频和视频文件来测试应用程序并开始刷新文件页面以查看文件是否已处理。然而,经过几次刷新后,UI 一直处于“正在加载”状态,直到 spring 批处理作业结束,然后按预期显示结果。

我运行了相同的测试并检查了网络选项卡,并注意到为检索文件列表而进行的调用处于“待处理”状态。它一直停留在该阶段,直到批处理的其余部分完成并返回成功 (200)。

在web服务端调试后发现文件服务应用卡在下面一行:

List<Asset> allAssets = jdbcTemplate.query("SELECT asset_id, asset_extension, asset_import_date, asset_imported_by_username, asset_name, asset_path, asset_preview_path, asset_thumbnail_path, asset_type FROM assets", new AssetRowMapper()); 

所以在我的场景中,当我刷新文件页面时,我试图访问资产表,因为春季批处理作业也在同一个表上工作以插入和更新每个文件相关数据。我想也许插入和更新查询以某种方式优先,并从我的选择查询中锁定表。所以我从 assets 表中创建了一个视图并将我的查询更改为:

List<Asset> allAssets = jdbcTemplate.query("SELECT asset_id, asset_extension, asset_import_date, asset_imported_by_username, asset_name, asset_path, asset_preview_path, asset_thumbnail_path, asset_type FROM assets_vw", new AssetRowMapper()); 

不幸的是,这不起作用,我得到了相同的结果。

要查看此问题是源自应用程序端(我的代码)还是数据库端,我运行了相同的测试,但我通过数据库上的 SQL Management Studio 手动运行了相同的查询。有趣的是,在几次成功的查询之后(正如我在 UI 上看到的那样),我得到了相同的结果,查询一直停留在“执行”阶段,直到其余的 spring 批处理作业完成。

看来问题出在数据库方面。

我检查了我的数据库上的最大连接数(如各种帖子中所建议的那样),它已经是默认值 0(无限制)。

此时我不确定我的数据库(或我的代码)出了什么问题。任何帮助将不胜感激。

【问题讨论】:

  • 发生这种情况时运行 sp_WhoIsActive。你可以谷歌一下,找到它的文档。可能发生的情况是您遇到阻塞。为了使 SQL Server 成为 ACID,必须在某些对象(行、表、方案)上工作时对其进行锁定。您可以查看这些锁、它们的优先级、共享锁、阻塞和死锁,以更好地理解它。我在这里解释得太深入了,许多专家都有数小时的视频或博客页面。

标签: sql-server spring-boot spring-batch spring-jdbc


【解决方案1】:

我同意@scsimon 的评论,即这些症状是由于阻塞造成的。默认情况下,SQL Server 在 READ COMMITTED 隔离级别中使用锁定,因此当您的 SELECT 查询(检索所有行)遇到尚未提交的正在进行上传的行时,它们可能会被阻止。

考虑开启READ_COMMITTED_SNAPSHOT 数据库选项,以便使用行版本控制而不是锁定READ COMMITTED 隔离级别而不更改应用程序代码。这是一个可行的解决方案,除非您的应用程序特别依赖锁定行为,例如使用 SQL Server 表作为队列时。 READ_COMMITTED_SNAPSHOT 的其他注意事项是增加 tempdb 使用(用于版本存储)和每行大小增加 14 字节。

我要补充一点,READ_COMMITTED_SNAPSHOT 在 Azure SQL 数据库中默认启用,但在本地版本中不启用。

【讨论】:

  • 运行查询ALTER DATABASE test_db SET READ_COMMITTED_SNAPSHOT ON WITH NO_WAIT; 有效。
【解决方案2】:

问题可能是您的大批量插入是在单个事务中执行的。对于处理插入的服务,我会检查代码以确保不是这种情况。如果是,则不需要所有插入都是全有或全无操作。您可以将单个文件的所有插入作为单个事务的一部分,而不是将所有文件都作为单个事务的一部分。这将确保来自您的 SELECT 语句的 READ LOCKS 将有机会进入队列并更及时地处理。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-11-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多