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



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

场景:

有两张桌子。LOCKED_FILE_INFO&文件信息

1:FILE_INFO表包含文件的信息,如FILEID(主)、FILENAME、USERID、FILETYPE、QTY

2:LOCKED_FILE_INFO表包含FILEID(主)、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: 如果文件已经存在(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秒,那么我根本不会遇到问题。

如有任何建议,我们将不胜感激。谢谢

问题的发生是因为逻辑中存在竞争条件。也就是说,由不同用户进行的两个并发事务可以在步骤a中成功执行检查,并尝试插入到LOCK_FILE_INFO。显然只有一个成功,第二个会失败。

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

你有几个选择如何处理你的问题。

处理主键冲突

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

使用更新锁定

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

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

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

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

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

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

不起作用的选项

通过synchronized进行同步

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

串行隔离级别

序列化隔离级别在这种情况下不起作用,因为它对插入没有帮助。如果在两个事务中插入两个具有相同密钥的记录,则无论隔离级别如何,都会得到相同的主键冲突。

最新更新