2016-01-15 4 views
0

Моя идея - реализовать базовые «векторные часы», где временные метки основаны на часах, всегда идут вперед и гарантированно будут уникальными.Как создать уникальные метки времени в PostgreSQL?

Например, в простой таблице:

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

Я использую триггер, чтобы установить значение временной метки перед вставкой. Это в основном просто идет в будущее, когда две вставки прибывают в то же самое время:

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(); 

Я тогда запустить огромное количество вставок в двух отдельных клиентов:

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 suggested смешивать current_clock() с SEQUENCE объекта, но это, вероятно, означает, избавившись от TIMESTAMP тип. Хотя я не могу понять, как это решит проблему изоляции ...

Есть ли общий шаблон, чтобы этого избежать?

Спасибо за ваши идеи :)

+1

Что случилось только с последовательностью? Вам действительно нужно время? Как насчет использования ключа на 2 столбцах (временная метка, последовательность)? В противном случае у вас есть VU UUID. – jcaron

+0

Почему вы просто не используете 'now()'? 'INSERT INTO timestamps now();' или установить значение по умолчанию этого поля как 'now()', в конце вы не можете вставлять значения duplicate now() при изменении каждой транзакции. – Solrac

+0

@SolracRagnarockradio, любые множественные вставки внутри одной и той же транзакции будут иметь одну и ту же метку времени. Также не уверен, что микросекундная точность временных меток гарантирует разные значения. – jcaron

ответ

0

Если у вас есть только один сервер Postgres, как вы сказали, я думаю, что с помощью метки + последовательности может решить эту проблему, так как последовательность отлично от транзакционной и соблюдать порядок вставки. Если у вас есть db shard, то это будет намного сложнее, но, возможно, может помочь распределенная последовательность 2ndquadrant в BDR, но я не думаю, что обыденность будет соблюдаться. Я добавил код ниже, если у вас есть настройка для его проверки.

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.stackexchange.com о потенциальном побочном эффекте.

+0

Может также иметь 2 столбца DATETIME и BIGSERIAL с индексом на обоих, чтобы вы могли быстро заказать .. Я предполагаю, что вы хотели, чтобы они основали время. – Rach

 Смежные вопросы

  • Нет связанных вопросов^_^