防止无序插入 Postgres 数据库

phi*_*dad 2 postgresql

我有一个系统,可以通过网络获取信息并将该信息放入 postgres 数据库中。信息只有一种“种类”,因此只有一张表。让我们像这样建立这个表的模型:

item: string
comment: jsonb
timestamp: datetime
Run Code Online (Sandbox Code Playgroud)

有很多项目,时间戳是由单独的系统生成的,然后再到达具有多个服务器的 Web 前端。我需要做的是保证插入到数据库中的给定item值的每条记录都比最后一条记录更新。如果它较旧,则不应将其添加到数据库中。

因此,为了澄清起见,假设有三个消息,如下所示:

Message 1:
{time: 1,
item: "A"
}
Message 2:
{time: 3,
item: "A"
}
Message 3:
{time: 2,
item: "b"
}
Run Code Online (Sandbox Code Playgroud)

如果消息按顺序到达1-2-3,则所有三个消息都会放入数据库中,因为消息 3 与消息 1 或 2 所针对的项目不同,因此不会进行比较。

如果消息按顺序到达,2-1-3则消息 1 不会放入数据库中,因为消息 2 是 item 的更新消息A

我想使用数据库进行此检查,以避免不同服务器之间的竞争条件。

我一直在阅读 PostgreSQL 文档,看起来我无法通过约束或排除来做到这一点。如何让数据库在插入记录之前进行顺序检查?

Sch*_*ern 5

假设你的桌子看起来像这样......

create table messages (
    item text not null,
    comment jsonb,
    created_at timestamp not null
);
Run Code Online (Sandbox Code Playgroud)

您可以使用触发器来执行此操作。

create or replace function check_only_newer_messages_for_item() returns trigger as $foo$
declare max_created_at_for_item timestamp;
begin
    max_created_at_for_item := (
        select coalesce(max(created_at), '-infinity'::timestamp)
        from messages
        where item = new.item
    );
    if max_created_at_for_item >= new.created_at then
        raise exception 'out of order message';
    end if;
    return new;
end;
$foo$ language plpgsql;

create trigger only_newer_messages_for_item
    before insert on messages
    for each row execute function check_only_newer_messages_for_item();

test=# insert into messages (item, created_at) values ('a', '2019-01-01');
INSERT 0 1
test=# insert into messages (item, created_at) values ('a', '2019-01-01');
ERROR:  out of order message
CONTEXT:  PL/pgSQL function check_only_newer_messages_for_item() line 10 at RAISE
test=# insert into messages (item, created_at) values ('b', '2018-01-01');
INSERT 0 1
test=# insert into messages (item, created_at) values ('b', '2018-01-02');
INSERT 0 1
test=# insert into messages (item, created_at) values ('b', '2018-01-01');
ERROR:  out of order message
CONTEXT:  PL/pgSQL function check_only_newer_messages_for_item() line 10 at RAISE
Run Code Online (Sandbox Code Playgroud)

item和上的综合索引created_at将有助于实现良好的效果。