我有一个服务器应用程序和一个数据库。服务器的多个实例可以同时运行,但所有数据都来自同一个数据库(在某些服务器上是postgresql,在其他情况下是ms sql server)。
在我的应用程序中,执行的过程可能需要数小时。我需要确保这个过程一次只执行一个。如果一个服务器正在处理,则在第一个服务器实例完成之前,其他任何服务器实例都无法处理。
该过程取决于一个表(我们称之为'ProcessTable')。我所做的是,在任何服务器启动长达一小时的过程之前,我在ProcessTable中设置一个布尔标志,表示该记录被“锁定”并正在处理(并非该表中的所有记录都被处理/锁定,所以我需要专门标记流程所需的每条记录)。因此,当前一个实例仍在处理时,当下一个服务器实例出现时,它会看到布尔标志并抛出异常。
问题是,两个服务器实例可能几乎同时被激活,当两者都检查ProcessTable时,可能没有设置任何标志,但两个服务器实际上都处于“设置”标志的过程中但由于事务尚未提交任何一个进程,因此任何进程都不会看到其他进程完成锁定。这是因为锁定机制本身可能需要几秒钟,因此存在两个服务器可能仍然能够同时处理的机会窗口。
看来我需要的是我的'Settings'表中的一条记录,它应该存储一个名为'LockInProgress'的布尔标志。因此,即使服务器可以锁定ProcessTable中所需的记录,它首先必须通过检查Settings表中的'LockInProgress'列来确保它具有完全的权限来执行锁定。
所以我的问题是,如何防止两台服务器同时修改设置表中的LockInProgress列?或者我是否以错误的方式处理此问题?
请注意我需要支持postgresql和ms sql server,因为有些服务器使用一个数据库,而有些服务器使用另一个。
提前致谢...
答案 0 :(得分:1)
如何先获取记录上的锁定,然后更新记录以显示“已锁定”。这将避免第二个实例成功获得锁定,从而更新记录失败。
重点是确保锁定并更新为一个原子步骤。
答案 1 :(得分:0)
创建一个存储过程来解锁,并在“可序列化”隔离下运行它。这将保证在任何给定时间只有一个进程可以获取资源。
请注意,这意味着尝试获取锁定的第二个进程将阻塞,直到第一个进程释放它。此外,如果您必须以这种方式获得多个锁,请确保该过程的设计保证将以相同的顺序获取和释放锁。这将避免死锁情况,其中两个进程在等待彼此释放锁的同时保留资源。
除非你不能处理阻塞的其他进程,否则这可能比试图实现'test and set'语义更容易实现和更强大。
答案 2 :(得分:0)
我一直在考虑这个问题,我认为这是最简单的做事方式;我只是执行这样的命令:
update settings set settingsValue = '333' where settingsKey = 'ProcessLock' and settingsValue = '0'
'333'将是每个服务器进程获取的唯一值(基于日期/时间,服务器名称,+随机值等)。
如果没有其他进程锁定表,则settingsValue将= = 0,该语句将调整settingsValue。
如果另一个进程已经锁定了该表,那么该语句将变为无操作,并且没有任何内容被修改。
然后我立即提交交易。
最后,我为settingsValue重新查询表,如果它是正确的值,那么我们的锁成功,我们继续,否则抛出异常,等等。当我们完成锁定后,我们重置了价值回落到0。
由于我使用的是SERIALIZATION交易模式,我看不出这会导致任何问题......如果我错了请纠正我。