Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/75.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql 引用表postgres的锁定行_Sql_Ruby On Rails_Database_Postgresql_Locking - Fatal编程技术网

Sql 引用表postgres的锁定行

Sql 引用表postgres的锁定行,sql,ruby-on-rails,database,postgresql,locking,Sql,Ruby On Rails,Database,Postgresql,Locking,我有一个表a,它被表B引用,也就是说a的模式如下所示: Table A ( id int, name varchar, ) 而表B的架构是: Table B ( id int, a_id int, val int ) 我有一段代码在表B中创建一条记录。但是,在竞争条件的情况下,比如说,在两个并行事务的情况下,我在该块中有一个条件失败,因此在表B中创建了两个记录,而不是一个记录。 事务块与此非常相似(在Rails中): ActiveRecord::Base.transact

我有一个表
a
,它被表
B
引用,也就是说
a
的模式如下所示:

Table A
(
  id int,
  name varchar,
)
而表
B
的架构是:

Table B
(
  id int,
  a_id int,
  val int
)
我有一段代码在表
B
中创建一条记录。但是,在竞争条件的情况下,比如说,在两个并行事务的情况下,我在该块中有一个条件失败,因此在表
B
中创建了两个记录,而不是一个记录。 事务块与此非常相似(在Rails中):

ActiveRecord::Base.transaction do
#a这里是模型a的ActiveRecord对象
b=b.new(a_id:a.id,val:value)#值为-ve
raise ActiveRecord::回滚,除非b.save
#此方法计算a的所有关联记录b的val之和。
#也就是说,从B中查找所有记录,其中B.a_id=a.id,并查找val之和
#纵队
总和=计算总和(a)
#以下条件在竞赛条件下失败

raise ActiveRecord::Rollback if sum如果理解您的困境,请尝试在BEGIN…COMMIT块内执行。对于大多数操作,这将取代锁。如果指令失败,则db保持不变。它对于多个表同时发生很大变化的操作特别有用。

解决此类并发问题的最通用解决方案是将整个块放在一个事务中。简单地说,这保证了您的事务的行为就像它们拥有对数据库的独占访问权一样。主要的缺点是,通过简单的
选择
,您可能会在任何时候触发序列化失败,如果发生这种情况,您应该准备重试事务。有一个与您的案例非常相似的例子,它应该能让您更好地了解这些交易在实践中的行为

除此之外,我认为您需要显式锁定某些内容。一种可能是通过
SELECT FOR UPDATE
语句将整个记录锁定在
A
中,这将阻止应用程序中的竞争进程,以及尝试在
B
中插入引用行的任何其他操作。这里的缺点是,您可能会阻止(或被阻止)一些不相关的操作,例如在不同的引用表中插入,或更新
a
本身

更好的方法可能是取出一个on
A.id
。这基本上等同于集中式散列,但这些锁的优点是由Postgres管理,并在提交/回滚时自动释放。需要注意的是,由于要对任意整数进行锁定,因此要确保不会与其他进程发生冲突,因为某些不相关的原因,这些进程恰好锁定了同一个整数

您可以通过使用两个参数版本的
pg_advision_xact_lock()
,并使用其中一个输入来标识锁的类型来处理此问题。我发现一个有用的策略不是在客户端的某个地方维护一堆锁类型常量,而是将对每个锁类型的调用封装在自己的函数中,并使用该函数作为类型标识符,例如:

CREATE FUNCTION lock_A_for_insert_into_B(a_id int) RETURNS VOID LANGUAGE sql AS $$
  SELECT pg_advisory_xact_lock('lock_A_for_insert_into_B(int)'::regprocedure::int, a_id)
$$
你有一个会阻塞的条件吗?数据库不是这样工作的。你什么都不做。他们做到了。为什么你的应用程序有条件地做任何事情?数据库确保了完整性,一切正常。集中的锁散列?我不知道你在干什么。。但是你走错了兔子洞,这是古纳

你得回过头来。快

CREATE TEMP TABLE a ( id_a int PRIMARY KEY, name text );
CREATE TEMP TABLE b ( id_b int PRIMARY KEY, id_a int REFERENCES a, val int );

WITH ti AS (
  INSERT INTO a (id_a, name) VALUES (2,'foo')
  RETURNING id_a
)
INSERT INTO b (id_b,id_a,val)
SELECT 1,ti.id_a,42
FROM ti;
结果,

TABLE a;
 id_a | name 
------+------
    2 | foo
(1 row)

test=# TABLE b;
 id_b | id_a | val 
------+------+-----
    1 |    2 |  42

您的意思是说
A
中的每条记录最多应被
B
中的一条记录引用吗?如果是这种情况,只需将引用列声明为
唯一的
。不,我不是这个意思。我已对问题进行了编辑。请使用这两行的示例更新您的问题。请使用SQL查询它,而不是示意性曲线括号format@VaoTsun我对答案进行了编辑,以包含事务块以及更好的表模式定义。BEGIN…COMMIT块仅确保您的操作始终一起执行或根本不执行。
TABLE a;
 id_a | name 
------+------
    2 | foo
(1 row)

test=# TABLE b;
 id_b | id_a | val 
------+------+-----
    1 |    2 |  42