Sql 拥有;选择“更新”选项;不存在行上的块

Sql 拥有;选择“更新”选项;不存在行上的块,sql,postgresql,Sql,Postgresql,我们在应用程序中有一些持久性数据,这些数据从服务器查询,然后存储在数据库中,这样我们就可以跟踪其他信息。因为我们不想在内存中使用对象时进行查询,所以我们会执行选择更新,以阻止其他想要获取相同数据的线程 我不确定select for update如何处理不存在的行。如果该行不存在,而另一个线程尝试在同一行上执行另一个选择更新,该线程将被阻止,直到另一个事务完成,还是也会得到一个空的结果集?如果它只得到一个空的结果集,有没有办法使它也阻塞,例如立即插入缺少的行 编辑: 因为有一句话,我们可能会锁定太

我们在应用程序中有一些持久性数据,这些数据从服务器查询,然后存储在数据库中,这样我们就可以跟踪其他信息。因为我们不想在内存中使用对象时进行查询,所以我们会执行
选择更新
,以阻止其他想要获取相同数据的线程

我不确定
select for update
如何处理不存在的行。如果该行不存在,而另一个线程尝试在同一行上执行另一个
选择更新
,该线程将被阻止,直到另一个事务完成,还是也会得到一个空的结果集?如果它只得到一个空的结果集,有没有办法使它也阻塞,例如立即插入缺少的行

编辑:

因为有一句话,我们可能会锁定太多,这里有一些关于我们案例中具体用法的更多细节。在简化的伪代码中,我们的程序流如下所示:

d = queue.fetch();
r = SELECT * FROM table WHERE key = d.key() FOR UPDATE;
if r.empty() then
  r = get_data_from_somewhere_else();

new_r = process_stuff( r );


if Data was present then
   update row to new_r
else
   insert new_r
此代码在多个线程中运行,从队列中获取的数据可能与数据库中的同一行有关(因此是锁)。但是,如果多个线程正在使用需要同一行的数据,则这些线程需要顺序化(顺序无关紧要)。但是,如果行不存在,这种顺序化就会失败,因为我们没有得到锁

编辑:

目前,我有以下解决方案,这对我来说似乎是一个丑陋的黑客

select the data for update
if zero rows match then
  insert some dummy data   // this will block if multiple transactions try to insert
  if insertion failed then
    // somebody beat us at the race
    select the data for update

do processing

if data was changed then
   update the old or dummy data
else
   rollback the whole transaction
然而,我既不是100%肯定这实际上解决了问题,也不是这个解决方案看起来很好。因此,如果有人必须提供更有用的东西,这将是伟大的

我不确定select for update如何处理不存在的行

没有

如果您知道新行的某些独特之处,那么最好使用建议锁。(如果需要,请使用hashtext(),并使用表的oid将其锁定。)

下一个最好的方法是桌锁


话虽如此,你的问题让人觉得你锁得太多了。只有在实际需要时才锁定行,即写入操作。

查看在第二次编辑中添加的代码,它看起来是正确的

至于它看起来像一个黑客,有两个选择-基本上它是所有关于移动数据库逻辑到数据库

一种是简单地将整个select放入更新,如果不存在,则在函数中插入逻辑,并改为执行
select get_对象(键1、键2等)

或者,您可以创建一个插入触发器,该触发器将忽略添加条目(如果该条目已经存在)的尝试,只需在执行“选择更新”之前进行插入即可。不过,这确实有可能干扰其他已经存在的代码

(如果我记得的话,我会在稍后检查我正在做什么时编辑并添加示例代码。)

示例解决方案(我还没有找到更好的:/)

线程A:

BEGIN;
SELECT pg_advisory_xact_lock(42); -- database semaphore arbitrary ID
SELECT * FROM t WHERE id = 1;
DELETE FROM t WHERE id = 1;
INSERT INTO t (id, value) VALUES (1, 'thread A');
SELECT 1 FROM pg_sleep(10); -- only for race condition simulation
COMMIT;
线程B:

BEGIN;
SELECT pg_advisory_xact_lock(42); -- database semaphore arbitrary ID
SELECT * FROM t WHERE id = 1;
DELETE FROM t WHERE id = 1;
INSERT INTO t (id, value) VALUES (1, 'thread B');
SELECT 1 FROM pg_sleep(10); -- only for race condition simulation
COMMIT;

原因总是正确的事务执行顺序。

感谢您提供有关建议锁的提示。从第一眼看,它似乎很有用,但我有点怀疑,因为文档中说咨询锁不绑定到事务,所以我们必须确保解锁它们(如果程序崩溃怎么办)。另外,我不认为我们锁定的太多(参见编辑)。这是正确的,但请检查Postgres 9.1:它引入了事务级别的建议锁,在提交或回滚时自动释放。很高兴知道。这意味着当我们切换到9.1时,可以减少很多逻辑。不幸的是,目前我们仍停留在8.4:(.LIKAO咨询锁在会话结束时发布,所以如果程序崩溃,DB终止连接时它们都被释放。”JD:很好地知道。因此,C++中的咨询锁和RAII的组合实际上可能能够在这种情况下拯救我们。关于您的编辑:您在哪里得到锁?@ CATLASK:UPS,我忘记了。select语句中的“FOR UPDATE”。因此,这就是锁定的位置。第一个建议实际上是不正确的:如果在其伪代码的insert之前添加一个sleep语句(在SQL函数中作为pg_sleep()),则会出现一个错误(dup键位于唯一的位置)将发生。SQL级别的异常处理也将无法锁定,但不会出错。不过,OP的当前编辑将完成任务。我实际上指的是第二次编辑,即“看起来像黑客”编辑。如果没有将其链接到下面的句子,这可能不明显。