使用数据库

时间:2015-04-28 15:24:41

标签: java database postgresql concurrency

我被要求实施“访问策略”以限制与数据库直接连接的应用程序(不是Web应用程序)中的证书过程的并发执行量。

应用程序在多台计算机上运行,​​如果多于用户尝试调用该进程,则在给定时间只允许执行一次,另一次必须返回错误消息(不等待第一次执行到端)。

虽然我使用 Java / Postgres ,但这是一个普遍的问题。

鉴于我在多台机器上运行相同的应用程序,我能想到的最简单的解决方案是实现某种“数据库标志”。

检查流程当前是否处于活动状态:

SELECT Active FROM Process

如果它处于活动状态,则返回“并发访问策略错误”。如果没有,请激活它:

UPDATE Process SET Active = 'Y'

执行完成后,只需更新活动标志:

UPDATE Process SET Active = 'N'

但是,我遇到了一个重大问题:

  • 如果我不使用数据库事务来更改活动标志,并且应用程序被终止,则活动标志将永远保留Y值。
  • 如果我使用数据库事务,第一点就解决了。但是,主机中活动标志的更改(从N到Y)只有在提交后才会显示,因此其他主机永远不会读取带有Y值的活动,因此无论如何都会执行。

有什么想法吗?

3 个答案:

答案 0 :(得分:1)

不要只有一个简单的Y / N标志,而是设置已设置为活动状态的时间戳,并让客户端应用程序定期设置它(比如每分钟或每五分钟)。然后,如果客户端崩溃,其他客户端将不得不等待超过该时间限制,然后假设客户端已经死亡并接管。这只是某种“心跳”机制,用于检查启动该进程的客户端是否还活着。

更简单的解决方案是将数据库配置为当时只接受一个连接吗?

答案 1 :(得分:1)

请勿使用活动标志,而只需根据用户ID锁定行。将该行锁定在专用事务/连接中。当其他用户尝试锁定行时(使用SELECT ... FOR UPDATE),您将收到错误,并且可以报告。

如果持有事务的进程失败,则释放锁。如果退出,则释放锁定。如果重新启动数据库,则释放锁定。

赢得所有人。

答案 2 :(得分:1)

我不确定RDBMS是否是解决此类问题的最佳系统。但是我最近在SQL Server 2012中实现了类似的东西。所以这就是我从那次经历中学到的东西。

一般来说,我的意思是原则上,你需要一个原子操作“检查值,更新(一个单一记录的)值”,即原子SELECT/UPDATE。这使事情变得复杂。并且因为通常在RDBMS中没有这样的标准单原子操作,所以您可以熟悉并使用ISOLATION LEVEL SERIALIZABLE

这是我在SQL Server 2012中实现它的方式,我已经认真测试过它,它工作正常。我有一个名为DistributedLock的表,其中的每条记录都代表一个逻辑锁。我允许的操作是tryLockreleaseLock(这些操作实现为两个存储过程)。 tryLock是非阻塞的(实际上是非阻塞的)。如果成功,它会向呼叫者返回一些ID /戳记,以后可以使用该ID /戳记来呼叫releaseLock。如果一个人在没有实际持有锁的情况下调用releaseLock(没有最新的ID /戳),则调用成功并且什么也不做,否则(如果调用者有锁),调用成功并释放由呼叫者,召集者。我也支持超时。因此,如果某个进程抓取给定锁定/记录的ID /戳记,并忘记释放它,它将在一段时间后自动过期。

这是表格的样子。

[DistributedLockID] [bigint] IDENTITY(1,1) NOT FOR REPLICATION NOT NULL -- surrogate PK 
[ResourceID] [nvarchar](256) NOT NULL -- resource/lock logical identifier 
[Duration] [int] NOT NULL 
[AcquisitionTime] [datetime] NULL 
[RecordStamp] [bigint] NOT NULL 

我想你可以弄明白其余的(或尝试,如果你遇到困难就打电话给我)。