首页 > 解决方案 > 使用 Spring、MyBatis 和 OracleDB 在 Java 中使用多个应用程序服务器进行数据库事务

问题描述

我在使用多个应用程序服务器在 Java 中执行数据库事务时遇到问题。

设想:

有两张桌子。LOCKED_FILE_INFO & FILE_INFO

1:FILE_INFO表包含FILEID(Primary)、FILENAME、USERID、FILETYPE、QTY等文件信息

2:LOCKED_FILE_INFO表包含FILEID(Primary)、FILENAME和TimeOfLock等信息

3:多个用户可以在FILE_INFO & LOCKED_FILE_INFO 表中输入文件列表或单个文件。

4:在进入 FILE_INFO 之前,我们将特定文件锁定在 LOCKED_FILE_INFO 中,这样只有 1 个用户可以锁定该 FILE 信息,然后进入 FILE_INFO 表。

5:如果多个用户对同一个文件执行条目,他们将得到-“信息已被另一个用户锁定”。

逻辑:

a: 在 FILE_INFO 表中进行条目之前,我们将检查 LOCKED_FILE_INFO 表以验证文件(例如:file100)是否已经存在

b:如果文件已经存在(LOCKED)-显示-“信息已被另一个用户锁定”。

c:如果文件不存在(NOT LOCKED),则进入LOCK_FILE_INFO,使其他用户无法锁定文件,只有成功的用户才能进入FILE_INFO表。

d:一旦我们进入 FILE_INFO 表,就从 LOCKED_FILE_INFO 表中删除锁定文件

问题:

当多个用户尝试在 LOCK_FILE_INFO 中同时锁定同一个文件时,我会收到 PRIMARY_KEY 违规异常这在我运行单个应用程序服务器时不会发生。这仅在运行多个应用程序服务器(至少 5 个)时发生

我尝试了几种方法 a:使用同步 b:使用事务级隔离

但是,当多个用户尝试同时插入 LOCKED_FILE_INFO 表时,我仍然无法锁定特定文件。但是,如果有至少 1 秒的延迟,那么我根本不会遇到问题。

任何建议将不胜感激。谢谢 !

标签: javaspringoraclewebspheremybatis

解决方案


出现问题是因为逻辑中存在竞争条件。即不同用户进行的两个并发事务可能会成功执行 check in stepa并尝试插入到LOCK_FILE_INFO. 显然只有一个成功,第二个会失败。

当并行度发生变化时(当同时运行的进程数量发生变化时),正在运行的进程中发生的各个事件的时间也会发生变化。因此,此类并发场景的行为可能会有所不同。

您有多种选择来解决您遇到的问题。

处理主键违规

您可以捕获异常并显示文件已锁定的消息。在这种情况下,检查锁记录是否存在是没有意义的。那就是您不需要执行 step a。只需插入一条锁记录,如果存在主键违规 - 锁已经存在。

锁定更新

使用插入锁定的问题是检测冲突的唯一原因是通过违反约束。如果您更改锁定策略以便更新记录。

首先,要么总是在创建记录时为文件创建一条记录,要么LOCK_FILE_INFO存储FILE_INFO有关锁定FILE_INFO表的信息(timeOfLock列应该足够,如果它是NULL文件未锁定)。

当需要加锁时,执行更新查询即可

update LOCK_FILE_INFO
set TimeOfLock = now()
where TimeOfLock is NULL
   AND FILEID = some_id

然后您需要检查记录是否已更新。每个修改语句都会返回更新记录的数量。要获得这个数字,只需int从 mybatis 映射器中的 insert 方法返回。如果记录被更新,则该事务成功获得锁,否则无法获得文件锁(要么已经被锁定,要么文件已被删除,见下文)。

some_id请注意,这依赖于正确的文件 ID的事实。在您执行更新语句之前可能会删除文件。在这种情况下,它看起来像文件被锁定,但实际上它已经消失了。实际上,这不是问题,因为在锁定失败后,您通常需要刷新 UI 以显示文件的更新状态,在这种情况下,您会检测到该文件已消失。

不起作用的选项

通过 synchronized 进行同步

使用synchronized关键字进行同步(如果正确完成)仅对单进程情况有帮助,因为在这种情况下,同步是使用进程内部的锁完成的。如果有多个 JVM 进程,每个进程都有自己的锁,同步将无法按预期工作。

序列化隔离级别

在这种情况下,序列化隔离级别将不起作用,因为它无法帮助插入。如果在两个事务中插入具有相同键的两条记录,则无论隔离级别如何,都会违反相同的主键。


推荐阅读