我被要求实施“访问策略”以限制与数据库直接连接的应用程序(不是Web应用程序)中的证书过程的并发执行量。
应用程序在多台计算机上运行,如果多于用户尝试调用该进程,则在给定时间只允许执行一次,另一次必须返回错误消息(不等待第一次执行到端)。
虽然我使用 Java / Postgres ,但这是一个普遍的问题。
鉴于我在多台机器上运行相同的应用程序,我能想到的最简单的解决方案是实现某种“数据库标志”。
检查流程当前是否处于活动状态:
SELECT Active FROM Process
如果它处于活动状态,则返回“并发访问策略错误”。如果没有,请激活它:
UPDATE Process SET Active = 'Y'
执行完成后,只需更新活动标志:
UPDATE Process SET Active = 'N'
但是,我遇到了一个重大问题:
有什么想法吗?
答案 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
的表,其中的每条记录都代表一个逻辑锁。我允许的操作是tryLock
和releaseLock
(这些操作实现为两个存储过程)。 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
我想你可以弄明白其余的(或尝试,如果你遇到困难就打电话给我)。