Postgres中

时间:2016-06-14 01:30:27

标签: postgresql npgsql optimistic-concurrency

我正在寻找一种方法来管理Postgres中多个表的乐观并发控制。我还试图保持数据库的业务逻辑 out 。我有一个像这样的表格设置:

CREATE TABLE master
(
    id SERIAL PRIMARY KEY NOT NULL,
    status VARCHAR NOT NULL,
    some_value INT NOT NULL,
    row_version INT NOT NULL DEFAULT(1)
)

CREATE TABLE detail
(
    id SERIAL PRIMARY KEY NOT NULL,
    master_id INT NOT NULL REFERENCES master ON DELETE CASCADE ON UPDATE CASCADE,
    some_data VARCHAR NOT NULL
)
每当行更新时,

master.row_version会自动增加一个触发器。

客户端应用程序执行以下操作:

  1. master表中读取记录。
  2. 根据记录的值计算一些业务逻辑,这可能包括涉及用户交互的几分钟延迟。
  3. 根据步骤2中的逻辑将记录插入detail表。
  4. 如果自从在步骤1读取记录后master.row_version的值发生了变化,我希望第3步被拒绝。乐观的并发控制在我看来似乎是正确的答案(唯一的答案?),但是我我不知道如何在这两个表中管理它。

    我认为Postgres中的一个函数在master表中的相关记录上有row-level lock可能是要走的路。但是我不确定这是否是我最好的/唯一的选择,或者看起来是什么样的(我在Postgres语法上有点绿色)。

    我正在使用Npgsql,因为客户端应用程序是用C#编写的。我不知道其中有什么可以帮助我吗?我想尽可能避免使用函数,但是我很难找到一种方法来使用直接SQL来执行此操作,并且匿名代码块(至少在Npgsql中)不支持I / O操作。需要。

2 个答案:

答案 0 :(得分:3)

如果您想使用乐观并发控制,则锁定已关闭,请参阅以下主题的the Wikipedia article

  

OCC假设多个交易可以经常完成   相互干扰。在运行时,事务使用数据   资源没有锁定这些资源。

您可以使用更复杂的INSERT语句。 如果$1是原row_version$2$3 master_idsome_data要插入detail,请运行< / p>

WITH m(id) AS
     (SELECT CASE WHEN master.row_version = $1
                  THEN $2
                  ELSE NULL
             END
      FROM master
      WHERE master.id = $2)
INSERT INTO detail (master_id, some_data)
   SELECT m.id, $3 FROM m

如果row_version已更改,则会尝试将NULL作为detail.id插入,这将导致 ERROR: null value in column "id" violates not-null constraint
您可以将其转换为更有意义的错误消息。

答案 1 :(得分:-2)

我已经得出结论,行锁可以用于“典型的”悲观并发控制方法,但是当与行版本结合使用时,可以产生具有一些有意义的好处的“混合”方法。

不出所料,悲观,乐观或“混合”并发控制的选择取决于应用程序的需求。

悲观并发控制

典型的悲观并发控制方法可能如下所示。

  1. 开始数据库事务。
  2. master表中读取(并锁定)记录。
  3. 执行业务逻辑。
  4. 将记录插入detail表。
  5. 提交数据库事务。
  6. 如果步骤3中的业务逻辑长时间运行,这种方法可能是不合需要的,因为它会导致长时间运行的事务(通常是不利的),以及master中记录的长期锁定可能在并发方面存在问题。

    乐观并发控制

    仅使用乐观并发控制的方法看起来可能更像这样。

    1. master表中读取记录(包括行版本)。
    2. 执行业务逻辑。
    3. 开始数据库事务。
    4. 增加master表中记录的行版本(乐观并发控制检查)。
    5. 将记录插入detail表。
    6. 提交数据库事务。
    7. 在这种情况下,数据库事务的保留时间较短,任何(隐式)行锁都是如此。 但是master表中记录的行版本的增量可能会对并发操作产生误导。想象一下这个场景的几个并发操作,他们将开始对乐观并发检查失败,因为行版本已经增加,即使记录上有意义的属性没有被更改。

      混合并发控制

      “混合”方法同时使用悲观锁定和(有点)乐观锁定。

      1. master表中读取记录(包括行版本)。
      2. 执行业务逻辑。
      3. 开始数据库事务。
      4. 根据{id>和行版本(排序的乐观并发控制检查)重新读取master表中的记录 AND 锁定行。< / LI>
      5. 将记录插入detail表。
      6. 提交数据库事务。
      7. 如果步骤4无法获取记录,则应将此视为乐观并发控制检查失败。记录自步骤1以来已更改,因此业务逻辑不再有效。

        与典型的悲观并发控制方案一样,这涉及事务和显式行锁,但事务+锁的持续时间不再包括执行业务逻辑所需的时间。

        与乐观并发控制方案一样,记录需要一个版本。但它的不同之处在于版本没有更新,这意味着取决于该行版本的其他操作不会受到影响。

        混合方法的示例

        混合方法可能有利的一个例子:

        博客有一个post表和comment表。仅当post.comments_locked标记为false时,才能将评论添加到帖子中。添加注释的过程可以使用混合方法,确保用户可以在没有任何并发​​异常的情况下同时添加注释。

        博客的所有者可以编辑他们的post,在这种情况下,可以采用传统的乐观并发控制方法。博客的所有者可以具有长时间运行的编辑过程,该过程不会受到添加评论的用户的影响。当post更新到数据库时,版本将递增,这意味着任何正在进行的注释添加操作都将失败,但可以使用数据库获取方法重新获取{{} {1}}从数据库中记录并重试该过程。