用多个应用程序服务器在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秒,那么我根本不会遇到问题。
任何建议将不胜感激。谢谢!
答案 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进程,则每个进程将拥有自己的锁,并且同步将无法按预期进行。
在这种情况下,串行隔离级别将起作用,因为它不能帮助插入。如果在两个事务中插入两个具有相同键的记录,则无论隔离级别如何,都会遇到相同的主键冲突。