Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/postgresql/10.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
锁定PostgreSQL表中的所有行_Postgresql_Locking - Fatal编程技术网

锁定PostgreSQL表中的所有行

锁定PostgreSQL表中的所有行,postgresql,locking,Postgresql,Locking,我试图在PG表中创建一种任务队列,类似于此,但有点复杂 1) 有些任务与某个实体id关联,当它们的实体id不同时,可以并行执行这些任务。所以他们有一张桌子: create table entity_tasks ( entity_id bigint, task text, inserted_at timestamp default now() ); 2) 有些任务必须以独占方式执行,即与所有其他任务按顺序执行。对于此类任务,还有一个表: create table block_ever

我试图在PG表中创建一种任务队列,类似于此,但有点复杂

1) 有些任务与某个
实体id
关联,当它们的
实体id
不同时,可以并行执行这些任务。所以他们有一张桌子:

create table entity_tasks (
  entity_id bigint,
  task text,
  inserted_at timestamp default now()
);
2) 有些任务必须以独占方式执行,即与所有其他任务按顺序执行。对于此类任务,还有一个表:

create table block_everything_tasks (
  task TEXT,
  inserted_at TIMESTAMP DEFAULT NOW()
);
block_everything_tasks
执行任务应阻止从
实体_tasks
和从
block_everything_tasks
执行所有任务

在一些原型设计之后,我还添加了一个表

create table entities_for_tasks (
  entity_id bigint primary key
);
每个实体获取和执行任务的工作方式如下:

begin;
    select entity_id into entity_to_lock
    from entities_for_tasks
    for update skip locked
    limit 1;

    select * from entity_tasks
    where entity_id = entity_to_lock
    order by inserted_at
    limit 1;

    -- execute them and delete from the `entity_tasks`
commit;
到目前为止还不错,但是当我试图实现从
块\u everything\u tasks
获取任务时,会变得很尴尬。我在这里看到了一些解决方案,但我不喜欢其中任何一个

1) 我可以显式地将整个
实体锁定到\u lock
表,如下所示

begin;
    lock table entity_to_lock;

    select * from block_everything_tasks
    order by inserted_at
    limit 1;

    -- execute them and delete from the `entity_tasks`
commit;
begin;
    with lock as (
      select * from entity_to_lock for update
    )
    select * from block_everything_tasks
    order by inserted_at
    for update skip locked
    limit 1;

    -- execute them and delete from the `entity_tasks`
commit;
但这将阻止将行添加到任务到
实体\u到\u锁
,并可能阻止将任务添加到其中一个队列

2) 或者我可以试着做类似的事情

begin;
    lock table entity_to_lock;

    select * from block_everything_tasks
    order by inserted_at
    limit 1;

    -- execute them and delete from the `entity_tasks`
commit;
begin;
    with lock as (
      select * from entity_to_lock for update
    )
    select * from block_everything_tasks
    order by inserted_at
    for update skip locked
    limit 1;

    -- execute them and delete from the `entity_tasks`
commit;
这看起来是个不错的解决方案,我不会阻止提交者,
entity\u to\u lock
也不会太大,但我不会使用
entity\u to\u lock
中的行,而且它们没有被锁定,所以它就是不起作用

所以我的问题是

  • 是否有方法将选项(1)中的
    实体锁定到\u lock
    表 这样插入仍然是可能的,
    select*from entity\u to\u lock
    哪里对于更新
    ,是否将被锁定
  • 或者有没有办法锁定选项(2)中的所有行而不实际使用这些行
  • 还是我应该在这里想出别的办法

插入和更新都会获取锁,因此您不会找到任何表级锁排除其中一个而不排除另一个

您可以使用
SELECT FOR UPDATE
锁定所有现有行,以防更改,但这不会同时影响
INSERT
ed记录,因此无论当前正在运行什么任务,它们仍然会被拾取和处理

将任务的
实体\u
表与
实体\u任务
保持同步也可能会出现问题,具体取决于您填充它的方式和使用的内容;这种模式在
SERIALIZABLE
以下的任何位置都容易出现竞争条件


退一步说,您确实需要解决两个截然不同的问题:创建和分配任务,以及协调任务的执行。第一个问题由基本排队机制完美处理,但试图通过重载该机制来解决第二个问题似乎是所有这些冲突的根源

因此,不要管队列,想想协调任务执行还需要什么:

  • 显示“任务正在运行”的锁
  • 一组锁,表示“任务正在针对实体
    x
    运行”
  • …其中,来自
    block\u everything\u tasks
    的任务需要独占锁定(1),而来自
    entity\u tasks
    的任务可以彼此共享锁定(1),但需要独占锁定(2)

    实现这一点最显式的方法是通过,它允许您“锁定”具有某些应用程序特定含义的任意整数

    假设没有实体的ID
    0
    ,让我们将其用于顶级“任务正在运行”锁。然后,在成功从队列中提取任务后,每个独占任务将运行:

    SELECT pg_advisory_xact_lock(0);
    
    SELECT pg_advisory_xact_lock_shared(0);
    SELECT pg_advisory_xact_lock(<entity_id of selected task>);
    
    …每个实体任务将运行:

    SELECT pg_advisory_xact_lock(0);
    
    SELECT pg_advisory_xact_lock_shared(0);
    SELECT pg_advisory_xact_lock(<entity_id of selected task>);
    
    …然后,对于独占任务:

    LOCK currently_processing;
    
    …对于每个实体的任务:

    INSERT INTO currently_processing VALUES (<entity_id of selected task>);
    <run the task>
    DELETE FROM currently_processing WHERE entity_id = <entity_id of selected task>;
    
    插入当前_处理值();
    从当前_处理中删除实体_id=;
    

    INSERT
    s将尝试获取表上的共享锁(被独占任务阻止),并且
    主键上的唯一索引将导致相同ID的并发
    INSERT
    s被阻止,直到冲突事务提交或回滚。

    是的,这是pg_advical_xact_lock_shared!我在寻找全局/每个实体具有某种等效rw锁的方法时忽略了它。谢谢