1

私の考えは、タイムスタンプがクロックベースであり、常に前進し、一意であることが保証されている基本的な «vector clock» を実装することです。

たとえば、単純なテーブルでは次のようになります。

CREATE TABLE IF NOT EXISTS timestamps (
    last_modified TIMESTAMP UNIQUE
);

トリガーを使用して、挿入前にタイムスタンプ値を設定します。基本的に、2 つの挿入が同時に到着すると、未来に進みます。

CREATE OR REPLACE FUNCTION bump_timestamp()
RETURNS trigger AS $$
DECLARE
    previous TIMESTAMP;
    current TIMESTAMP;
BEGIN
     previous := NULL;
     SELECT last_modified INTO previous
      FROM timestamps
     ORDER BY last_modified DESC LIMIT 1;

     current := clock_timestamp();
     IF previous IS NOT NULL AND previous >= current THEN
        current := previous + INTERVAL '1 milliseconds';
     END IF;
     NEW.last_modified := current;
     RETURN NEW;
END;
$$ LANGUAGE plpgsql;

DROP TRIGGER IF EXISTS tgr_timestamps_last_modified ON timestamps;

CREATE TRIGGER tgr_timestamps_last_modified
BEFORE INSERT OR UPDATE ON timestamps
FOR EACH ROW EXECUTE PROCEDURE bump_timestamp();

次に、2 つの別々のクライアントで大量の挿入を実行します。

DO
$$
BEGIN
    FOR i IN 1..100000 LOOP
       INSERT INTO timestamps DEFAULT VALUES;
    END LOOP;
END;
$$;

予想どおり、衝突が発生します。

ERROR: duplicate key value violates unique constraint "timestamps_last_modified_key"
État SQL :23505
Détail :Key (last_modified)=(2016-01-15 18:35:22.550367) already exists.
Contexte : SQL statement "INSERT INTO timestamps DEFAULT VALUES"
PL/pgSQL function inline_code_block line 4 at SQL statement

@rach はオブジェクトと混合current_clock()することを提案しましたSEQUENCEが、それはおそらくTIMESTAMP型を取り除くことを意味します。分離の問題をどのように解決するかはわかりませんが...

これを回避する一般的なパターンはありますか?

あなたの洞察をありがとう:)

4

2 に答える 2

1

私の 2 セント ( http://tapoueh.org/blog/2013/03/15-batch-updateから着想を得たもの)。

大量の挿入の前に次を追加してみてください。

LOCK TABLE timestamps IN SHARE MODE;

公式ドキュメントはこちら: http://www.postgresql.org/docs/current/static/sql-lock.html

于 2016-01-15T19:29:51.817 に答える
0

あなたが言ったようにPostgresサーバーが1つしかない場合、シーケンスは非トランザクションであり、挿入順序を尊重するため、タイムスタンプ+シーケンスを使用すると問題を解決できると思います。db shard がある場合は、はるかに複雑になりますが、BDR の 2ndquadrant の分散シーケンスが役立つ可能性がありますが、順序性が尊重されるとは思いません。テストするためのセットアップがある場合は、以下にいくつかのコードを追加しました。

CREATE SEQUENCE "timestamps_seq";

-- Let's test first, how to generate id.
SELECT extract(epoch from now())::bigint::text || LPAD(nextval('timestamps_seq')::text, 20, '0') as unique_id ;

           unique_id
--------------------------------
 145288519200000000000000000010
(1 row)


CREATE TABLE IF NOT EXISTS timestamps (
    unique_id TEXT UNIQUE NOT NULL DEFAULT extract(epoch from now())::bigint::text || LPAD(nextval('timestamps_seq')::text, 20, '0')
);


INSERT INTO timestamps DEFAULT VALUES;
INSERT INTO timestamps DEFAULT VALUES;
INSERT INTO timestamps DEFAULT VALUES;

select * from timestamps;
           unique_id
--------------------------------
 145288556900000000000000000001
 145288557000000000000000000002
 145288557100000000000000000003
(3 rows)

それがうまくいくかどうか教えてください。私は DBA ではないので、潜在的な副作用についても dba.stackexchange.com で質問するとよいでしょう。

于 2016-01-15T19:23:02.667 に答える