简体   繁体   English

数据库记录锁定

[英]database record locking

I have a server application, and a database. 我有一个服务器应用程序和一个数据库。 Multiple instances of the server can run at the same time, but all data comes from the same database (on some servers it is postgresql, in other cases ms sql server). 服务器的多个实例可以同时运行,但所有数据都来自同一个数据库(在某些服务器上是postgresql,在其他情况下是ms sql server)。

In my application, there is a process that is performed which can take hours. 在我的应用程序中,执行的过程可能需要数小时。 I need to ensure that this process is only executed one at a time. 我需要确保这个过程一次只执行一个。 If one server is processing, no other server instance can process until the first one has completed. 如果一个服务器正在处理,则在第一个服务器实例完成之前,其他任何服

The process depends on one table (let's call it 'ProcessTable'). 该过程取决于一个表(让我们称之为'ProcessTable')。 What I do is, before any server starts the hour-long process, I set a boolean flag in the ProcessTable which indicates that this record is 'locked' and is being processed (not all records in this table are processed / locked, so I need to specifically mark each record which is needed by the process). 我所做的是,在任何服务器启动长达一小时的过程之前,我在ProcessTable中设置一个布尔标志,表示该记录被“锁定”并正在处理(并非该表中的所有记录都被处理/锁定,所以我需要专门标记流程所需的每条记录)。 So when the next server instance comes along while the previous instance is still processing, it sees the boolean flags and throws an exception. 因此,当前一个实例仍在处理时,当下一个服务器实例出现时,它会看到布尔标志并抛出异常。

The problem is, that 2 server instances might both be activated at nearly the same time, and when both check the ProcessTable, there may not be any flags set, but both servers are actually in the process of 'setting' the flags but since the transaction hasn't yet commited for either process, neither process will see the locking done by the other process. 问题是,两个服务器实例可能几乎同时被激活,并且当两者都检查ProcessTable时,可能没有设置任何标志,但是两个服务器实际上都在“设置”标志的过程中但是因为事务尚未提交任何一个进程,任何进程都不会看到其他进程完成锁定。 This is because the locking mechanism itself may take a few seconds, so there is that window of opportunity where 2 servers might still be able to process at the same time. 这是因为锁定机制本身可能需要几秒钟,因此存在两个服务器可能仍然能够同时处理的机会窗口。

It appears that what I need is a single record in my 'Settings' table which should store a boolean flag called 'LockInProgress'. 看来我需要的是我的'Settings'表中的一条记录,它应该存储一个名为'LockInProgress'的布尔标志。 So before even a server can lock the needed records in the ProcessTable, it first must make sure that it has full rights to do the locking by checking the 'LockInProgress' column in the Settings table. 因此,即使服务器可以锁定ProcessTable中所需的记录,它首先必须通过检查Settings表中的'LockInProgress'列来确保它具有完全的权限来进行锁定。

So my question is, how do I prevent two servers from both modifying that LockInProgress column in the settings table, at the same time... or am I going about this in the wrong manner? 所以我的问题是,如何阻止两台服务器同时修改设置表中的LockInProgress列?或者我是否以错误的方式处理此问题?

Please note that I need to support both postgresql and ms sql server as some servers use one database, and some servers use the other. 请注意,我需要支持postgresql和ms sql server,因为有些服务器使用一个数据库,而有些服务器使用另一个。

Thanks in advance... 提前致谢...

How about obtaining a lock on the record first and then update the record to show "locked". 如何首先获取记录锁定然后更新记录以显示“已锁定”。 This would avoid the 2nd instance to get a lock successfully and thereby the update of record fails. 这将避免第二个实例成功获得锁定,从而更新记录失败。

The point is to make sure the lock and update as one atomic step. 重点是确保锁定并更新为一个原子步骤。

Make a stored procedure that hands out the lock, and run it under 'serializable' isolation. 创建一个存储过程来解锁,并在“可序列化”隔离下运行它。 This will guarantee that one and only one process can get at the resource at any given time. 这将保证在任何给定时间只有一个进程可以获取资源。

Note that this means that the second process trying to get at the lock will block until the first process releases it. 请注意,这意味着尝试获取锁定的第二个进程将阻塞,直到第一个进程释放它。 Also, if you have to get multiple locks in this manner, make sure that the design of the process guarantees that the locks will be acquired and released in the same order. 此外,如果您必须以这种方式获得多个锁,请确保该过程的设计保证将以相同的顺序获取和释放锁。 This will avoid deadlock situations where two processes hold resources while waiting for each other to release locks. 这将避免死锁情况,其中两个进程在等待彼​​此释放锁时保持资源。

Unless you can't deal with your other processes blocking this would probably be easier to implement and more robust than attempting to implement 'test and set' semantics. 除非你不能处理你的其他进程阻塞,否则这可能比试图实现'test and set'语义更容易实现和更强大。

I've been thinking about this, and I think this is the simplest way of doing things; 我一直在考虑这个问题,我认为这是最简单的做事方式; I just execute a command like this: 我只是执行这样的命令:

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

'333' would be a unique value which each server process gets (based on date/time, server name, + random value etc). '333'将是每个服务器进程获得的唯一值(基于日期/时间,服务器名称,+随机值等)。

If no other process has locked the table, then the settingsValue would be = to 0, and that statement would adjust the settingsValue. 如果没有其他进程锁定表,则settingsValue将= = 0,该语句将调整settingsValue。

If another process has already locked the table, then that statement becomes a no-op, and nothing get's modified. 如果另一个进程已经锁定了该表,那么该语句将成为无操作,并且没有任何内容被修改。

I then immediately commit the transaction. 然后我立即提交交易。

Finally, I requery the table for the settingsValue, and if it is the correct value, then our lock succeeded and we continue on, otherwise an exception is thrown, etc. When we're done with the lock, we reset the value back down to 0. 最后,我重新查询表的settingsValue,如果它是正确的值,那么我们的锁成功,我们继续,否则抛出异常,等等。当我们完成锁定后,我们将值重新设置为到0。

Since I'm using SERIALIZATION transaction mode, I can't see this causing any issues... please correct me if I'm wrong. 由于我正在使用SERIALIZATION交易模式,我无法看到这导致任何问题...如果我错了请纠正我。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM