0

У меня есть следующая таблица:Postgres: как добавить уникальный идентификатор в таблице

CREATE TABLE myid 
(
    nid bigserial NOT NULL, 
    myid character varying NOT NULL, 
    CONSTRAINT myid_pkey PRIMARY KEY (myid) 
) 

Теперь я хочу, чтобы добавить записи в эту таблицу с помощью следующей функции:

CREATE FUNCTION getmyid(_myid character varying) 
    RETURNS bigint AS 
$BODY$ --version 1.1 2015-03-04 08:16 
DECLARE 
    p_nid bigint; 
BEGIN 
    SELECT nid INTO p_nid FROM myid WHERE myid=_myid FOR UPDATE; 
    IF NOT FOUND THEN 
    INSERT INTO myid(myid) VALUES(_myid) RETURNING nid INTO p_nid; 
    END IF; 
    RETURN p_nid; 
END;$BODY$ 
    LANGUAGE plpgsql VOLATILE 
    COST 100; 

Обычно это работает отлично, но при высокой нагрузке эта функция иногда терпит неудачу, поскольку «дублирующее значение ключа нарушает уникальное ограничение» myid_pkey »; Эта функция вызывается из триггера при вставке в другую таблицу, а вставки вызывается внутри транзакции. Уровень изоляции установлен для READ COMMITED , postgres 9.1 на Debian Wheezy. Что я делаю неправильно?

+1

Почему * если *? Что не так с 'INSERT INTO myid (myid) VALUES (_myid) ГДЕ НЕ СУЩЕСТВУЕТ (SELECT 1 FROM myid WHERE myid = _myid);'? [можно сделать в простом SQL, без необходимости в plpgsql] – wildplasser

+0

@wildplasser Я собирался прокомментировать то же :) –

+0

@wildplasser: это все равно может привести к «дублированию значения ключа», если две сессии делают это точно в том же (но я согласен, что это более эффективно) –

ответ

1

Я вижу следующим образом как это происходит.

  1. Два процесса (нитки) вызывают функцию одновременно с тем же myid.
  2. Оба потока успешно выполняют запрос SELECT nid INTO .., и см. - в таблице нет такого myid.
  3. Обе нити идут в IF NOT FOUND THEN
  4. резьбы 1 выполняет INSERT INTO myid(myid) и фиксирует транзакцию без ошибок
  5. резьбы 2 выполняет INSERT INTO myid(myid) и терпит неудачу, потому что тот же myid значение уже существует в таблице (первичный ключ ограничения).

Почему Thread 2 видит другие транзакционные данные в собственной транзакции? Из-за явлений «неизменяемого чтения», которые возможны при изоляции READ COMMITTED (http://www.postgresql.org/docs/9.2/static/transaction-iso.html).

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