2010-02-25 159 views
4

我有一个服务器应用程序和一个数据库。服务器的多个实例可以同时运行,但所有数据都来自同一个数据库(在某些服务器上是postgresql,在其他情况下是ms sql服务器)。数据库记录锁定

在我的应用程序中,有一个过程需要几个小时才能完成。我需要确保这个过程一次只能执行一次。如果一台服务器正在处理,则其他服务器实例不能处理,直到第一个服务器实例完成。

该过程依赖于一个表(我们称之为'ProcessTable')。我所做的是,在任何服务器启动长达一小时的过程之前,我在ProcessTable中设置一个布尔标志,表示此记录已被“锁定”并正在处理中(并未处理/锁定此表中的所有记录,所以我需要专门标记过程所需的每条记录)。因此,当下一个服务器实例出现而前一个实例仍在处理时,它会看到布尔标志并引发异常。

问题是,两个服务器实例可能几乎同时被激活,并且当两者都检查ProcessTable时,可能没有设置任何标志,但两个服务器实际上都在“设置”标志,但由于事务尚未提交任一进程,因此这两个进程都不会看到其他进程完成的锁定。这是因为锁定机制本身可能需要几秒钟的时间,所以有两个服务器仍然可以同时处理的机会窗口。

看来,我需要的是我的'设置'表中应该存储一个名为'LockInProgress'的布尔标志的单个记录。因此,在即使服务器可以锁定ProcessTable中所需的记录之前,首先必须通过检查Settings表中的'LockInProgress'列来确保它具有完全锁定权限。

所以我的问题是,如何防止两个服务器同时修改设置表中的LockInProgress列......或者我以错误的方式解决这个问题?

请注意,我需要支持postgresql和MS SQL服务器,因为有些服务器使用一个数据库,而有些服务器使用另一个。

在此先感谢...

回答

0

作出这样的手了锁的存储过程,并在“序列化”隔离运行它。这将保证在任何特定时间只有一个进程可以获取资源。

请注意,这意味着试图锁定第二个进程将阻塞,直到第一个进程释放它。另外,如果您必须以这种方式获取多个锁,请确保该过程的设计可确保以相同的顺序获取并释放锁。这将避免两个进程在等待彼​​此释放锁时保存资源的死锁情况。

除非你不能处理你的其他进程阻塞这可能会比实现'测试和设置'的语义更容易实现和更强大。

+0

这是一个更好的主意,然后从Pradeep的答案,如果是这样,为什么?看起来,这可能会起作用,但它似乎也有点复杂(可能更少一些“跨平台”)......我可能是错的...... – user85116 2010-02-25 19:01:31

+0

您可能需要为每个支持的平台编写不同的sproc ,但编写某种插件机制以支持少数特定于平台的操作并不困难。这种方法的关键在于它使用本地锁定机制和语义(即一个进程被阻塞直到锁定变为空闲)。 – ConcernedOfTunbridgeWells 2010-02-26 07:29:16

1

如何先获取记录上的锁定,然后更新记录以显示“锁定”。这将避免第二个实例成功获得锁,从而更新记录失败。

重点是确保锁定和更新作为一个原子步骤。

+0

你可以扩大一点吗?我在前端使用jdbc ...是java.sql.Connection.TRANSACTION_SERIALIZABLE我在这里之后? – user85116 2010-02-25 18:59:39

+0

我已经在Oracle PL/SQL过程中做了几年。首先你在行上获得一个锁。成功锁定时,检查该值是否已被锁定,否则更新为“锁定”并释放锁定。这将确保第二个线程只有在第一个线程释放它之后才会获得锁定,并且现在将被“锁定”。 – Pradeep 2010-02-26 22:29:30

+0

引用此: “事务隔离级别的一个示例是TRANSACTION_READ_COMMITTED,它不允许直到它被提交之后访问该值。换句话说,如果事务隔离级别设置为TRANSACTION_READ_COMMITTED,则DBMS不会允许发生脏读,接口Connection包含五个值,它们表示您可以在JDBC中使用的事务隔离级别。“ 来自链接:http://java.sun.com/j2se/1.4.2/docs/api/java/sql/Statement.html – Pradeep 2010-02-26 22:47:54

0

我一直在想这个,我认为这是最简单的做事方式;我只是执行命令是这样的:

update settings set settingsValue = '333' where settingsKey = 'ProcessLock' and settingsValue = '0' 

“333”将是一个独特的价值,每个服务器进程获得(基于日期/时间,服务器名称+随机值等)。

如果没有其他进程锁定表,那么settingsValue将= = 0,并且该语句将调整settingsValue。

如果另一个进程已经锁定了表,那么该语句就变成了no-op,并且没有得到修改。

然后我立即提交交易。

最后,我查询了settingsValue的表,如果它是正确的值,那么我们的锁成功了,我们继续,否则抛出异常等等。当我们完成锁定时,我们重置值减少到0.

由于我使用的是SERIALIZATION事务模式,所以我看不到任何问题......请纠正我,如果我错了。