为更新实现“序列号”,而不是日期时间;postgres 专门

Fat*_*tie 4 postgresql sequence amazon-rds

所以是一个 postgres 数据库(特别是一个 AmazonRDS 实例 fwiw)。

安排事情以获取任何记录的最后更新(或创建)时间很容易。

该信息将是日期时间。

我想要实现的是:想象一个整数,sequenceNumber,它对数据库来说是原子的和全局的。

简单地说,每个表都会有一个 sequenceNumber 字段。

任何时候任何记录(即,在任何表中)被更新(或创建),它都会获得下一个序列号。

您知道如何拥有对数据库一无所知的程序员朋友。海浪。我的白痴解决方案是,在整个代码库中,每次创建或修改记录时,一定要记得更新它的序列号。(当然,只需要一个全局系统来给出下一个原子序列号。)

对此的声音解决方案是什么?

事实上,它是否已经以某种我不知道的方式完全可用,或者?

ype*_*eᵀᴹ 9

正如a_horse_with_no_name最初在问题评论中建议的那样:

--assuming we have tables: a, b, ...
CREATE TABLE a
(
  a_id integer NOT NULL PRIMARY KEY,
  a_name text
) ;

CREATE TABLE b
(
  b_id integer NOT NULL PRIMARY KEY,
  b_name text
) ;
Run Code Online (Sandbox Code Playgroud)

首先我们创建一个全局序列并在每个表中添加一列:

-- create one global sequence to be used by all tables
CREATE SEQUENCE global_sequence_number ;

-- add a column in each table, that uses the same sequence
ALTER TABLE a
  ADD COLUMN global_sequence_id INT NOT NULL 
    DEFAULT nextval('global_sequence_number'::regclass) ;

ALTER TABLE b
  ADD COLUMN global_sequence_id INT NOT NULL 
    DEFAULT nextval('global_sequence_number'::regclass) ;
Run Code Online (Sandbox Code Playgroud)

创建触发器函数(一个)和UPDATE触发器(每个表一个):

-- create a trigger function to update the column
CREATE OR REPLACE FUNCTION update_global_sequence_id()
RETURNS TRIGGER AS 
$$
BEGIN
   NEW.global_sequence_id = nextval('global_sequence_number'::regclass); 
   RETURN NEW;
END;
$$ language 'plpgsql';

-- add a trigger, in each table
CREATE TRIGGER update_a_global_sequence_id
  BEFORE UPDATE ON a FOR EACH ROW 
    EXECUTE PROCEDURE update_global_sequence_id();

CREATE TRIGGER update_b_global_sequence_id
  BEFORE UPDATE ON b FOR EACH ROW 
    EXECUTE PROCEDURE  update_global_sequence_id();
Run Code Online (Sandbox Code Playgroud)

和利润:

 -- example of use
INSERT INTO a (a_id, a_name)
VALUES 
  (1, 'a'),  (2, 'b') ;

INSERT INTO b (b_id, b_name)
VALUES 
  (3, 'c'),  (4, 'd'),  (5, 'e') ;

SELECT *
FROM a FULL JOIN b ON FALSE 
ORDER BY COALESCE(a.global_sequence_id, b.global_sequence_id) ;
Run Code Online (Sandbox Code Playgroud)
-  输出
  1 一个 1
  2 b 2 
             3 c 3
             4 天 4
             5 和 5

在一些更新之后,检查触发器是否工作:

-- lets do some updates:
UPDATE a SET a_name = 'aa' WHERE a_id = 1 ;
UPDATE b SET b_name = 'cc' WHERE b_id = 3 ;

-- and see what happened
SELECT *
FROM a FULL JOIN b ON FALSE 
ORDER BY COALESCE(a.global_sequence_id, b.global_sequence_id) ;
Run Code Online (Sandbox Code Playgroud)
-  输出
  2 b 2 
             4 天 4
             5 和 5
  1 aa 6 -- 序列更新为 6
             3 cc 7 -- 序列更新为 7


Pau*_*ite 6

社区 Wiki 回答记录了Craig Ringer对该问题的评论

关于这一点的快速警告。如果您打算这样做,以便您的应用程序可以记住“上次看到的序列号”并根据序列号进行某种同步或批处理,则它会丢失行。事务的提交顺序不一定与它们调用的顺序相同nextval

具体来说,如果三个 xacts A、B 和 C 的值分别为 1、2 和 3,那么它们几乎都在同一时间提交,C 可能首先变得可见,因此您会在表中看到 3,但 1 和 2 不是那里呢。诸如此类的事情。PostgreSQL 的逻辑解码通过为您提供严格提交顺序的数据流来解决这个问题,以防这与您正在处理的问题有关。

您调用nextval(...)以获取序列值的顺序不一定与 xacts 提交的顺序相同,除非应用程序在外部强制执行该顺序。未提交的行对 不可见SELECT。所以你可能SELECT * FROM the_table WHERE the_sequence_column > 0因为你上次看到的 ID 是 0。你发现the_sequence_column返回的最大值是 3。所以下次你SELECT ... WHERE the_sequence_column > 3. 但实际上,1并且2可能你看到 3之后就犯了,所以你没有在第一个中得到它们SELECT,你也不会在后者中得到它们。

您可能会想“我只会保留一个我还没有看到的 ID 列表”。但这会表现得很糟糕,而且它也无法应对回滚的 xact,在服务器崩溃和重新启动期间跳过序列值等。它们永远不会被重用并被丢弃。因此,您的应用程序将继续寻找永远不会存在的值。除非您可以让您的应用程序强制执行提交顺序,以便它按照分配 ID 的顺序提交(如果一个 xact 获得 ID 1、4 和 7,而另一个 xact 获得 ID 2 和 3,这可能是不可能的)...您的解决方案不会真正起作用,这就是为什么您可能希望研究逻辑解码,以稳健、有序、可靠的方式从数据库中获取变更流。例如,有一些插件可以流式传输 json。