是否需要一种即使重新启动应用程序也不会丢失的锁定机制永久锁定?

问题描述

我有以下情况。有2个共享数据库的应用程序。这两个应用程序均可用于更改基础数据库。例如,可以从两个系统中修改客户1。我想确保当有人对应用程序1中的客户1执行操作时,我需要该锁的持久锁,以便应用程序2中的任何人都不能对同一客户执行任何操作。即使这些应用程序中的任何一个出现故障,它仍应保持锁定状态。解决此类问题的正确方法是什么?

解决方法

正如@ Turing85的注释所暗示的那样,这是非常危险的领域:如果有人通过电源线绊倒,则您的应用程序将无法运行,无法再次启动。永久。至少在有人进入并手动解决问题之前。这很少是您想要的。

通常的解决方案是在数据库级别进行锁定:如果它是“单个文件即数据库”模型(例如H2或SQLite),则让数据库引擎锁定文件以进行写入,并处理OS级文件锁定用作您的门控机制。这样做具有很大的优势,即如果应用A由于某种原因(电源不足,严重崩溃,谁知道)突然退出应用,则该锁将被放弃。

如果数据库是一个单独的运行进程(psql,mysql,mssql等),则这些数据库具有可以使用的锁定功能。

如果这些选项均不可用,则可以手动滚动它:您可以使用保证原子/唯一的新文件API来制作文件:

int pid = 0; // see below
Path p = Paths.get("/absolute/path/to/agreed/upon/lockfile/location/lockfile.pid");
Files.write(p,String.valueOf(pid),StandardOpenOption.CREATE_NEW);

CREATE_NEW打开选项正在要求Java确保原子性:[A]文件以前不存在,现在存在,并且是由该过程导致的,或者是[B]将被抛出。 / p>

没有[C]这个进程创建了它,但是另一个进程很不幸地同时在做同一件事,并且也创建了它,并且其中一个进程现在正在覆盖另一个进程的工作-这就是CREATE_NEW的意思并保证:这不会发生。 (vs CREATE选项将覆盖其中的内容,并且不保证原子性。)

您现在可以将文件系统用作全局唯一锁:要获取该锁,请创建该文件。如果可以的话,太好了。你说对了。如果不能,则必须等待(如果您希望尽快获取它,则需要使用watcher API或循环,这不是一个好选择,与in相比,这是一个非常昂贵的操作) -process锁定!)-要放弃锁定,只需删除文件即可。

为防止硬崩溃将文件永久保留在那里,从而阻止您的应用再次运行,它可以帮助在其中注册“ pid”(进程ID)。如果您是手动解决问题,这会给您一些调试信息,并且您可以用来自动检查(“嘿,操作系统,是否还有运行ID为123981的进程?否?好吧,那么它肯定已经崩溃了并且离开了将文件锁定到位!')。不幸的是,在Java中使用pid令人费解,因为Java或多或少是围绕您不应过多地依赖底层操作系统的概念设计的,并且Java并未真正假设“进程ID”是底层操作系统的东西做。谷歌搜索如何获取它,您可以执行此操作。

这将我们带到了终点,您显然会担心不一致:毕竟,您实际上似乎很希望看到这样的疯狂想法,即您希望在发生严重崩溃(进程崩溃和崩溃)时永久禁用该应用程序没有明确放弃锁定)。我假设您要这样做是因为您担心数据库处于不一致状态,并且您不希望任何东西再次触摸它,除非您手动查看它。

好吧,好吧,锁定文件业务正是您如何获得的。但是,这是用户敌对的,并且不是必需的:您可以设计数据库和流程(使用事务,仅追加表和日记系统),以便它们始终可以在硬崩溃中幸免。

例如,考虑文件系统。在过去的旧棕褐色调中,当您绊倒电源线时,然后在启动时会看到一个讨厌的东西,系统会执行“全盘检查”,很可能会发现很多错误。 >

但是在现代系统中,情况已不再如此。整日跳越该功率卡。您不会得到损坏的文件(除非流程设计不当,在这种情况下损坏是应用程序的问题,而不是文件系统的问题),并且不需要进行大量的磁盘检查。

这主要是通过称为“新闻”的概念来实现的。

说,您要替换一个读为“ Hello,World!”的文件。和“再见!”字样。您可以开始写字节。假设您进入“ Goodb,世界!”然后有人通过电缆绊倒。

您现在正在软管。数据不一致,谁知道发生了什么。

但是想想一个不同的系统:

日记

系统首先创建一个名为“ .jobrecord”的文件,并在其中写入:我将打开该文件,并以“再见,现在!”开头覆盖数据。

然后,它实际上继续执行该操作。

然后,它以原子方式删除作业记录(例如,通过更新单个字节以标记为“ done”)。

现在,在启动时,系统可以检查该文件是否存在,如果存在,则检查该作业是否已完成,或者在需要时完成该文件。瞧,现在您永远不会有一个不一致的系统。

您也可以编写此类工具。

替代:仅追加

另一种滚动方式是仅添加数据,并且具有有效性标记。因此,您永远不会覆盖任何文件,只创建新文件,然后将它们“旋转到位”。例如,创建一个名为“ target.new”的新文件,而不是覆盖该文件,然后复制该数据,然后用“ Goodbye,now!”覆盖开始,然后以原子方式将文件重命名为原始“ target” ”,这样就保证了原始文件不会受到损害,并且在某一时刻,目标文件的“视图”是旧文件,而在另一个原子跟踪时刻,是新文件,在此之间没有任何时间间隔。在两点之间。

数据库中的类似概念是永远不要更新,只能插入,具有递增的原子计数器,并且知道“当前状态”始终是计数器编号最高的行。

重点是:有多种构建健壮系统的方法,除非外力破坏您的数据存储,否则这些方法永远不会导致数据不一致。