Postgres的增量触发原子和高并发安全吗?

时间:2018-07-22 16:20:20

标签: postgresql postgresql-9.5

进行以下设置:

CREATE EXTENSION IF NOT EXISTS "pgcrypto";

CREATE TABLE IF NOT EXISTS foo (
  id TEXT DEFAULT gen_random_uuid () NOT NULL,
  text TEXT NOT NULL,
  is_latest BOOLEAN DEFAULT TRUE,
  version INTEGER NOT NULL DEFAULT 0,
  updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE UNIQUE INDEX foo_id_idx ON foo (id, is_latest);
CREATE INDEX foo_updated_at_idx ON foo (updated_at);
CREATE INDEX foo_created_at_idx ON foo (created_at);

CREATE OR REPLACE FUNCTION foo_copy_row ()
  RETURNS TRIGGER
AS $BODY$
BEGIN
  NEW.version = OLD.version + 1;
  NEW.is_latest = TRUE;
  NEW.updated_at = NOW();
  NEW.created_at = OLD.created_at;
  INSERT INTO foo (id, text, is_latest, version, updated_at, created_at)
  VALUES (OLD.id, OLD.text, NULL, OLD.version, OLD.updated_at, OLD.created_at);
  RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql;

CREATE TRIGGER COPY BEFORE
UPDATE
  ON foo FOR EACH ROW EXECUTE PROCEDURE foo_copy_row ();

我能够成功地对数据进行版本控制,并且每次更新都自动增加version列。

我的问题是,当我在同一行上进行高并发更新时,我期望ORDER BY id, version DESCORDER BY id, updated_at DESC相同,但它们却不相同。

这是我更新行的方式:

INSERT INTO foo (text) VALUES ('hello')
RETURNING *;

UPDATE foo SET text = 'welcome'
WHERE id = 'some-uuid' AND is_latest = TRUE
RETURNING *;

这是结果的一个例子:

SELECT id, is_latest, version, updated_at, created_at FROM foo ORDER BY id, updated_at DESC;

-

                 id                  | is_latest | version |          updated_at           |          created_at
-------------------------------------+-----------+---------+-------------------------------+-------------------------------
4d2339ba-eb1f-4925-a4bc-753f2994bd5f | t         |       4 | 2018-07-22 16:12:55.702035+00 | 2018-07-22 16:12:55.694725+00
4d2339ba-eb1f-4925-a4bc-753f2994bd5f |           |       2 | 2018-07-22 16:12:55.698144+00 | 2018-07-22 16:12:55.694725+00
4d2339ba-eb1f-4925-a4bc-753f2994bd5f |           |       1 | 2018-07-22 16:12:55.697429+00 | 2018-07-22 16:12:55.694725+00
4d2339ba-eb1f-4925-a4bc-753f2994bd5f |           |       3 | 2018-07-22 16:12:55.697157+00 | 2018-07-22 16:12:55.694725+00
4d2339ba-eb1f-4925-a4bc-753f2994bd5f |           |       0 | 2018-07-22 16:12:55.694725+00 | 2018-07-22 16:12:55.694725+00

缺少的是什么?

是事务的触发部分,并且UPDATE锁是否保留到执行BEFORE和AFTER为止?

是否有可能以相同的id和版本号结尾两行?

1 个答案:

答案 0 :(得分:3)

这并不奇怪。 now()返回事务开始的时间。无法保证首先开始的事务将是第一个执行触发器的事务。

使用版本确定更新顺序。