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"; эта функция вызывается из триггера при вставке в другую таблицу, а вставки вызываются внутри транзакции. Уровень изоляции устанавливается для ЧИТАЙТЕ КОММЕНТАРИЙ, postgres 9.1 на Debian Wheezy. Что я делаю не так?

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 значение уже существует в таблице (ограничение PRIMARY KEY).

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

Другие вопросы по тегам