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 ответ
Следующим образом вижу, как это происходит.
- Два процесса (потока) вызывают функцию одновременно с одним и тем же
myid
, - Оба потока успешно выполняются
SELECT nid INTO ..
запросите, и увидите - такого нетmyid
в таблице сейчас. - Обе темы идут в
IF NOT FOUND THEN
- Поток 1 выполняется
INSERT INTO myid(myid)
и совершает транзакцию без ошибок - Поток 2 выполняется
INSERT INTO myid(myid)
и терпит неудачу, потому что жеmyid
значение уже существует в таблице (ограничение PRIMARY KEY).
Почему поток 2 видит данные, зафиксированные в другой транзакции, в собственной транзакции? Из-за явлений "неповторяемого чтения", что возможно при изоляции READ COMMITTED ( http://www.postgresql.org/docs/9.2/static/transaction-iso.html).