【问题标题】:Concurrency Postgresql UPDATE并发 Postgresql 更新
【发布时间】:2020-01-11 21:02:51
【问题描述】:

我在线程进程中遇到了这种情况。有表格样例:

id   |   type  | external_key | balance | amount  |      date
1       TOPUP       MT1                    10         2019-01-1 13:01:00.500110
2       USAGE       MT1                    -1         2019-01-1 13:01:01.300100
3       TOPUP       MT3                     5         2019-01-1 13:01:02.400300

每行上的每个新数据我都会运行此触发器来更新字段余额以存储每一行​​的当前余额:

create or replace function function_update_balance() returns trigger
    language plpgsql
as $$
BEGIN
    IF new.type = 'CBA_ADJ' THEN
    update example set balance=(SELECT SUM(case when amount >= 0 then amount when amount <= 0 then amount end) FROM example WHERE id=id and type=new.type and external_key =new.external_key ) WHERE id=new.id and date<= new.date;
    END IF;
return null;
END;
$$
;

如果行插入速度不快(不像线程),则余额具有正确的余额,如下所示:

id   |   type  | external_key | balance | amount  |      date
1       TOPUP       MT1            10       10         2019-01-1 13:01:00.500110
2       USAGE       MT1             9       -1         2019-01-1 13:01:01.300100
3       TOPUP       MT3             5        5         2019-01-1 13:01:02.400300

但是当数据插入线程时(只是在 per , milisecond 上不同,余额没有像这样的正确结果:

id   |   type  | external_key | balance | amount  |      date
1       TOPUP       MT1            10       10         2019-01-1 13:01:00.500110
2       USAGE       MT1             9       -1         2019-01-1 13:01:01.300100
3       TOPUP       MT3             5        5         2019-01-1 13:01:02.400300
4       USAGE       MT1             8        -1        2019-01-1 13:01:03.404000
5       USAGE       MT1             8        -1        2019-01-1 13:01:02.405000
6       USAGE       MT1             8        -1        2019-01-1 13:01:03.407000
7       USAGE       MT1             8        -1        2019-01-1 13:01:03.408000
8       USAGE       MT1             4        -1        2019-01-1 13:01:05.612000

就像,更新时的选择在最后一个事务提交之前读取了相同的值,我尝试过这样的隔离:

create or replace function function_update_balance() returns trigger
        language plpgsql
    as $$
    BEGIN
        IF new.type = 'CBA_ADJ' THEN
        start transaction isolation level repeatable read;
        update example set balance=(SELECT SUM(case when amount >= 0 then amount when amount <= 0 then amount end) FROM example WHERE id=id and type=new.type and external_key =new.external_key ) WHERE id=new.id and date<= new.date;
        END IF;
    return null;
    END;
    $$
    ;

然后给我错误:

ERROR: cannot begin/end transactions in PL/pgSQL
Hint: Use a BEGIN block with an EXCEPTION clause instead.
Where: PL/pgSQL function function_update_currentbalance() line 5 at SQL statement

关于并发进程这个问题的任何线索?

【问题讨论】:

    标签: postgresql sql-update plpgsql


    【解决方案1】:

    这是一个奇怪的触发函数。

    但是,正如您所注意到的,并发插入会导致触发器函数同时运行,因此它们无法看到彼此的数据修改。

    如果你需要避免这种情况,你必须使用锁序列化执行。

    你可以

    LOCK example IN SHARE ROW EXCLUSIVE MODE;
    

    在触发函数中,但由于这会阻止并发 autovacuum 运行,所以这不是一个热门的想法。

    最好使用行锁:

    PERFORM 1 FROM example
    WHERE type = NEW.type and external_key = NEW.external_key
    ORDER BY id, date
    FOR UPDATE OF example;
    

    这将序列化所有影响相同行的事务。

    【讨论】:

      猜你喜欢
      • 2017-03-31
      • 2013-09-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-08-02
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多