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
Sql Postgres的并发性和可序列化性。我需要一个可序列化的隔离级别吗?_Sql_Postgresql_Transactions - Fatal编程技术网

Sql Postgres的并发性和可序列化性。我需要一个可序列化的隔离级别吗?

Sql Postgres的并发性和可序列化性。我需要一个可序列化的隔离级别吗?,sql,postgresql,transactions,Sql,Postgresql,Transactions,我有一个项目和作业表: 项目 id=PK job_id=Jobs FK 状态=进行中|完成 工作 id=PK 项目开始时是进行中的,但对其执行工作,然后交给工作人员进行更新。我有一个更新程序进程,在项目进入时用新状态更新项目。到目前为止,我所采用的方法是(伪代码): 这有意义吗?我希望它在并发环境中工作。我现在的问题是,对于一个作业,COMPLETE是多次到达的,而我只想对COMPLETE执行一次逻辑 如果将事务级别更改为SERIALIZABLE,则会出现如下所述的“错误:由于事务之间的

我有一个项目和作业表:

项目

  • id=PK
  • job_id=Jobs FK
  • 状态=进行中|完成
工作

  • id=PK
项目开始时是进行中的,但对其执行工作,然后交给工作人员进行更新。我有一个更新程序进程,在项目进入时用新状态更新项目。到目前为止,我所采用的方法是(伪代码):

这有意义吗?我希望它在并发环境中工作。我现在的问题是,对于一个作业,COMPLETE是多次到达的,而我只想对COMPLETE执行一次逻辑

如果将事务级别更改为SERIALIZABLE,则会出现如下所述的“错误:由于事务之间的读/写依赖关系而无法序列化访问”错误

因此,我的问题是:

  • 我需要序列化吗
  • 我可以选择更新吗?在哪里
  • 有人能给我解释一下发生了什么,为什么

编辑:我重新打开了这个问题,因为我对前面的答案不满意。有人能为我解释一下吗?具体地说,我需要一些关于伪代码的示例查询。

您可以在
项目和
作业上使用
选择更新
,并在单个事务中处理两个表中受影响的行。这应该足以强制执行整个操作的完整性,而不会产生
SERIALIZABLE
或表锁的开销

我建议您创建一个函数,在
items
表上进行插入或更新后调用该函数,传递该项的主键:

CREATE FUNCTION process_item(item integer) RETURNS void AS $$
DECLARE
    item items%ROWTYPE;
    job  jobs%ROWTYPE;
BEGIN  -- Implicitly starting a transaction
    SELECT * INTO job FROM jobs
    WHERE id = (SELECT job_id FROM items WHERE id = item)
    FOR UPDATE;  -- Lock the row for other users

    FOR item IN SELECT * FROM items FOR UPDATE LOOP      -- Rows locked
        -- Work on items individually 

        UPDATE items
        SET status = 'COMPLETED'
        WHERE id = item.id;
    END LOOP;

    -- Do any work on the job itself
END;  -- Implicitly close the transaction, releasing the locks
$$ LANGUAGE plpgsql;

如果某个其他进程已在该作业或其任何关联项上工作,则执行将停止,直到释放该其他锁为止。这与
SERIALIZABLE
不同,后者将一直工作到失败,然后您必须在第二次尝试中重新执行所有处理。

如果您希望作业能够同时运行,则
SERIALIZABLE
SELECT FOR UPDATE
都不会直接工作

如果使用
SELECT FOR UPDATE
锁定行,则另一个进程将在执行
SELECT FOR UPDATE
时简单地阻塞,直到第一个进程提交事务

如果执行
SERIALIZABLE
,则两个进程可以同时运行(处理同一行),但在执行
COMMIT
时,至少应该有一个进程失败,因为数据库将检测到冲突。此外,如果与数据库中同时进行的影响相关行的任何其他查询发生冲突,则可序列化的
可能会失败。使用
SERIALIZABLE
的真正原因正是为了防止其他作业同时进行数据库更新,而不是阻止同一作业执行两次

注意,有一些技巧可以让
选择更新
跳过锁定行。如果您这样做,那么您就可以拥有实际的并发性。看


我经常看到的另一种方法是将“status”列更改为第三个临时状态,在处理作业时使用该临时状态。通常情况下,会有“待定”、“进行中”、“完成”等状态。当您的流程搜索要做的工作时,它会找到一个“挂起”作业,立即将其移动到“进行中”并提交事务,然后继续工作,最后将其移动到“完成”。缺点是,如果进程在处理作业时死亡,它将无限期地留在“进行中”。

谢谢,这是一个非常好的答案。因此,在连接上选择更新将锁定我需要的所有内容?除非您明确指出要在哪些表上应用锁,否则查询中的所有表都将锁定所有受影响的行。
CREATE FUNCTION process_item(item integer) RETURNS void AS $$
DECLARE
    item items%ROWTYPE;
    job  jobs%ROWTYPE;
BEGIN  -- Implicitly starting a transaction
    SELECT * INTO job FROM jobs
    WHERE id = (SELECT job_id FROM items WHERE id = item)
    FOR UPDATE;  -- Lock the row for other users

    FOR item IN SELECT * FROM items FOR UPDATE LOOP      -- Rows locked
        -- Work on items individually 

        UPDATE items
        SET status = 'COMPLETED'
        WHERE id = item.id;
    END LOOP;

    -- Do any work on the job itself
END;  -- Implicitly close the transaction, releasing the locks
$$ LANGUAGE plpgsql;