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_Data Integrity - Fatal编程技术网

PostgreSQL的乐观锁定

PostgreSQL的乐观锁定,postgresql,data-integrity,Postgresql,Data Integrity,问题陈述:我正在使用Repository模式通过pgnpm模块将记录从PostgreSQL DB(版本11)拉入并更新到Node.JS API中。如果两个用户几乎同时尝试修改同一记录,则第一个提交用户所做的更改将被第二个提交用户覆盖。我想阻止第二个用户的更改被提交,直到他们用第一个用户的更改更新了记录的本地副本 我知道像CouchDB这样的DB有一个“_rev”属性,它在本例中使用该属性来检测从过时快照更新的尝试。随着我研究的深入,我发现这被称为乐观锁定()。因此,我将向表中添加一个rev列,并

问题陈述:我正在使用Repository模式通过
pg
npm模块将记录从PostgreSQL DB(版本11)拉入并更新到Node.JS API中。如果两个用户几乎同时尝试修改同一记录,则第一个提交用户所做的更改将被第二个提交用户覆盖。我想阻止第二个用户的更改被提交,直到他们用第一个用户的更改更新了记录的本地副本

我知道像CouchDB这样的DB有一个“_rev”属性,它在本例中使用该属性来检测从过时快照更新的尝试。随着我研究的深入,我发现这被称为乐观锁定()。因此,我将向表中添加一个rev列,并在SQL update语句中使用它

UPDATE tableX
SET field1 = value1,
    field2 = value2,
    ...,
    rev_field = uuid_generate_v4()
WHERE id_field = id_value
  AND rev_field = rev_value
但是,如果
id\u值
匹配且
rev\u值
不匹配,则不会告诉我的存储库代码记录已过时,只有0行受查询影响

因此,我有一个用pgAdmin编写的脚本,它将检测
更新影响0行的情况,然后检查rev_字段

DO $$
DECLARE
    i_id numeric;
    i_uuid uuid;
    v_count numeric;
    v_rev uuid;
BEGIN
    i_id := 1;
    i_uuid := '20b2e135-42d0-4a49-94c0-5557dd09abd1';

    UPDATE account_r
    SET account_name = 'savings',
        rev = uuid_generate_v4()
    WHERE account_id = i_id
      AND rev = i_uuid;

    GET DIAGNOSTICS v_count = ROW_COUNT;
    
    IF v_count < 1 THEN
        SELECT rev INTO v_rev
        FROM account_r
        WHERE account_id = i_id;
        
        IF v_rev <> i_uuid THEN
            RAISE EXCEPTION 'revision mismatch';
        END IF;
    END IF;
    
    RAISE NOTICE 'rows affected: %', v_count;
END $$;
DO$$
声明
i_id数字;
i_uuid uuid;
v_计数数字;
v_rev uuid;
开始
i_id:=1;
i_uuid:=“20b2e135-42d0-4a49-94c0-5557dd09abd1”;
更新帐户\u r
设置帐户名称='储蓄',
rev=uuid\u generate\u v4()
其中account\u id=i\u id
和rev=i_uuid;
获取诊断v_计数=行计数;
如果v_计数小于1,则
在v_rev中选择rev
来自账户
其中account_id=i_id;
如果v_rev i_uuid那么
引发异常“修订不匹配”;
如果结束;
如果结束;
提出通知“受影响的行:%”,v_计数;
完(元);
虽然我可以将这段代码改编成一个存储过程并从节点调用它,但我希望有一个解决方案不会那么复杂。一方面,将这些函数移动到DB将清理我的JS代码,另一方面,这是大量要编写的样板SQL,因为必须为每个表执行
UPDATE
DELETE


有没有更简单的方法来完成这件事?(也许代码是更简单的方法?)我应该看看ORM来帮助减少这里的头痛吗?

没有必要维护
rev
值。您可以获取表行的md5哈希

fiddle包括其他查询,以证明计算的
md5()
是基于行的值

使用该哈希进行乐观锁定,更新可以采用以下形式:

update mytable
   set some_int = -1
 where id = 1
   and md5(mytable::text) = <md5 hash from select>
returning *
更新mytable
设置一些_int=-1
其中id=1
和md5(mytable::text)=
返回*
您仍然需要检查无返回行,但这可以在节点端抽象出来


它似乎包含受影响的行数,因此您不需要返回*
部分。

谢谢,这似乎是避免额外字段的一种有用方法。我仍然需要运行第二个查询,以确定“受影响的0行”是来自ID上的不匹配还是来自MD5上的不匹配。但这可能与在节点中进行二次检查一样好。鲜为人知的是,如果使用
可重复读取
隔离级别,您会自动获得乐观锁定。@LaurenzAlbe我不知道这在这种情况下会有什么帮助,因为数据已从数据库中提取出来<代码>可重复读取如果事务同时发生,看起来会有所帮助。但在API的情况下,一个客户端可能会向用户呈现数据,并从UI获取不再适用的更改,因为另一个用户同时已经更改了记录。要求您在同一事务中读取和写入数据,因此是的,如果其间存在用户交互,则该技术不适用。
update mytable
   set some_int = -1
 where id = 1
   and md5(mytable::text) = <md5 hash from select>
returning *