Multithreading 并发插入和更新postgres

Multithreading 并发插入和更新postgres,multithreading,postgresql,concurrency,Multithreading,Postgresql,Concurrency,当您在多线程环境中收到同一字段的并发插入和更新时,有没有解决问题的方法 范例 线程1 BEGIN insert into users (name,age) values('spiderman',27) COMMIT 线程2 BEGIN update into users set age = 26 where name='spiderman'; COMMIT 大多数情况下,更新(事务)不知道发生了插入,因此会抛出错误 我在这里看到的是竞争条件的经典示例,避免竞争条件的一种方法是使用有效锁

当您在多线程环境中收到同一字段的并发插入和更新时,有没有解决问题的方法

范例

线程1

BEGIN 
insert into users (name,age) values('spiderman',27)
COMMIT
线程2

BEGIN
 update into users set age = 26 where name='spiderman';
COMMIT
大多数情况下,更新(事务)不知道发生了插入,因此会抛出错误

我在这里看到的是竞争条件的经典示例,避免竞争条件的一种方法是使用有效锁

如何对数据库中尚不存在的记录应用锁(记录)


在上面的insert语句示例中,Update知道记录还不在DB中。

基于锁的解决方案将非常困难,可能不值得费心。您必须找到一种方法来创建一组包含所有可能的“名称”字段的数据,然后围绕该数据创建一个建议锁定系统。它也可能容易出错

但是,如果问题真的是要确保
更新在插入
之后才会运行,那么有更简单的解决方案

具体来说,我想到的是:

BEGIN;
  SELECT count(*) FROM users WHERE name = 'spiderman';
  -- on the application side, check to see what the result is
  -- if it's 0, ROLLBACK
  -- if it's >0:
  UPDATE users SET age = 26 WHERE name = 'spiderman';
COMMIT;
如果您最终回滚,您将返回并稍后重试,这可能意味着一些简单的操作,如
睡眠
10秒并重试,或者更复杂的操作,如将工作项放回队列并继续执行不同的工作


如果您担心在
SELECT
UPDATE
之间发生更新,您可以
SELECT FOR UPDATE
,这将锁定所选行,直到事务完成。

基于锁定的解决方案将非常困难,可能不值得费事。您必须找到一种方法来创建一组包含所有可能的“名称”字段的数据,然后围绕该数据创建一个建议锁定系统。它也可能容易出错

但是,如果问题真的是要确保
更新在插入
之后才会运行,那么有更简单的解决方案

具体来说,我想到的是:

BEGIN;
  SELECT count(*) FROM users WHERE name = 'spiderman';
  -- on the application side, check to see what the result is
  -- if it's 0, ROLLBACK
  -- if it's >0:
  UPDATE users SET age = 26 WHERE name = 'spiderman';
COMMIT;
如果您最终回滚,您将返回并稍后重试,这可能意味着一些简单的操作,如
睡眠
10秒并重试,或者更复杂的操作,如将工作项放回队列并继续执行不同的工作


如果您担心在
SELECT
UPDATE
之间发生更新,您可以
SELECT FOR UPDATE
,这将锁定所选行,直到事务完成。

在web环境中,使用(请参阅JPA中的@Version注释)在web环境中,使用(请参阅JPA中的@Version注释)我们通常没有这种情况,所以引入睡眠会影响几乎所有的人。我不能在更新时选择,因为这里发生的并发操作是插入和更新。Select不是查询计划的一部分。我能想到的一种方法是将事务隔离级别设置为diry uncommitted。对此,您有何看法?您使用的是什么版本的postgresql?如果是9.5,那么现在就有了一个“upsert”功能,它的工作原理与SETNX类似。语法如下:
在用户(姓名、年龄)值('spiderman',26)中插入冲突(姓名)DO UPDATE SET age=26
这能满足您的需要吗?我的印象是,
upsert
对concurrent
insert
保持正确,至少这是我在提出问题之前阅读它时理解的。我不确定我是否理解您最近的评论
SETNX
让人困惑,我认为——只有在键不存在的情况下才会设置值。如果它真的存在,那么它将一事无成。如果
SETNX foo 27
,然后
SETNX foo 26
,则“foo”的值将为27,因为它存在。这是你想要的行为吗?因为这似乎与你在原始帖子中描述的有所不同。我最初认为您希望两个线程都能成功地对数据库进行更改。我已经提供了几种方法来做到这一点。这就是你所需要的吗?我们通常没有这种情况,所以引入睡眠会影响几乎所有人。我不能在更新时选择,因为这里发生的并发操作是插入和更新。Select不是查询计划的一部分。我能想到的一种方法是将事务隔离级别设置为diry uncommitted。对此,您有何看法?您使用的是什么版本的postgresql?如果是9.5,那么现在就有了一个“upsert”功能,它的工作原理与SETNX类似。语法如下:
在用户(姓名、年龄)值('spiderman',26)中插入冲突(姓名)DO UPDATE SET age=26
这能满足您的需要吗?我的印象是,
upsert
对concurrent
insert
保持正确,至少这是我在提出问题之前阅读它时理解的。我不确定我是否理解您最近的评论
SETNX
让人困惑,我认为——只有在键不存在的情况下才会设置值。如果它真的存在,那么它将一事无成。如果
SETNX foo 27
,然后
SETNX foo 26
,则“foo”的值将为27,因为它存在。这是你想要的行为吗?因为这似乎与你在原始帖子中描述的有所不同。我最初认为您希望两个线程都能成功地对数据库进行更改。我已经提供了几种方法来做到这一点。这就是你需要的吗?