使用Spring,MyBatis和OracleDB的具有多个应用程序服务器的Java中的数据库事务

时间:2018-10-23 21:20:09

标签: java spring oracle websphere mybatis

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

场景:

有两个表。 LOCKED_FILE_INFO和FILE_INFO

1:FILE_INFO表包含诸如FILEID(主),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:如果多个用户对同一个FILE执行输入,则他们将得到-“信息已被另一用户锁定”。

逻辑:

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

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

c:如果文件不存在(未锁定),请在LOCK_FILE_INFO中输入一个内容,以便其他用户无法锁定该文件,只有成功的用户才能进入FILE_INFO表。

d:在FILE_INFO表中输入后,从LOCKED_FILE_INFO表中删除锁定文件

问题:

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

我尝试过的方法很少 答:使用同步 b:使用事务级别隔离

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

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

1 个答案:

答案 0 :(得分:1)

发生此问题是因为逻辑中有一个race condition。即,由不同用户进行的两个并发事务可以在步骤a中成功执行检查,并尝试插入到LOCK_FILE_INFO中。显然只有一个成功,第二个失败。

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

您可以选择几种方法来解决所遇到的问题。

处理违反主键的行为

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

使用更新锁定

使用insert进行锁定的问题在于,检测冲突的唯一原因是违反约束。如果您更改锁定策略以便改为更新记录。

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

当您需要锁定时,只需执行更新查询

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

然后,您需要检查记录是否已更新。每个修改语句都会返回更新的记录数。要获取此数字,只需从mybatis映射器中的插入方法中返回int。如果记录已更新,则此事务成功获取了锁,否则无法获取文件锁(已被锁定或文件已删除,请参见下文)。

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

无效的选项

通过同步进行同步

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

串行隔离级别

在这种情况下,串行隔离级别将起作用,因为它不能帮助插入。如果在两个事务中插入两个具有相同键的记录,则无论隔离级别如何,都会遇到相同的主键冲突。