PostgreSQL без пробелов - PullRequest
       9

PostgreSQL без пробелов

20 голосов
/ 03 апреля 2012

Я перехожу из MySql в Postgres, и я заметил, что при удалении строк из MySql уникальные идентификаторы для этих строк повторно используются при создании новых. В Postgres, если вы создаете строки и удаляете их, уникальные идентификаторы больше не используются.

Есть ли причина такого поведения в Postgres? Могу ли я заставить его вести себя как MySql в этом случае?

1 Ответ

46 голосов
/ 03 апреля 2012

Последовательности имеют пробелы для одновременных вставок. Попытка избежать пропусков или повторного использования удаленных идентификаторов создает ужасные проблемы с производительностью. См. FAQ по вики PostgreSQL .

PostgreSQL SEQUENCE с используются для распределения идентификаторов. Они только увеличиваются и освобождаются от обычных правил отката транзакций, позволяющих нескольким транзакциям получать новые идентификаторы одновременно. Это означает, что если транзакция откатывается, эти идентификаторы «выбрасываются»; нет списка «свободных» идентификаторов, только текущий счетчик идентификаторов. Последовательности также обычно увеличиваются, если база данных отключается нечистым способом.

Синтетические ключи (идентификаторы) в любом случае не имеют значения . Их порядок незначителен, их единственное свойство значимости - уникальность. Вы не можете осмысленно измерить, насколько «далеко друг от друга» находятся два идентификатора, и при этом вы не можете осмысленно сказать, больше или меньше один из них. Все, что вы можете сделать, это сказать «равно» или «не равно». Все остальное небезопасно. Вы не должны заботиться о пробелах.

Если вам нужна последовательность без промежутков, которая повторно использует удаленные идентификаторы, у вас может быть одна, вам просто нужно отказаться от огромного количества производительности для нее - в частности, у вас не может быть никакого параллелизма на INSERT с вообще потому что вы должны отсканировать таблицу на предмет наименьшего свободного идентификатора, заблокировав таблицу для записи, чтобы никакая другая транзакция не смогла получить такой же идентификатор. Попробуйте поискать "postgresql gapless sequence".

Самый простой подход - использовать таблицу счетчиков и функцию, которая получает следующий идентификатор. Вот обобщенная версия, которая использует таблицу счетчиков для генерации последовательных идентификаторов без пробелов; хотя он не использует идентификаторы повторно.

CREATE TABLE thetable_id_counter ( last_id integer not null );
INSERT INTO thetable_id_counter VALUES (0);

CREATE OR REPLACE FUNCTION get_next_id(countertable regclass, countercolumn text) RETURNS integer AS $$
DECLARE
    next_value integer;
BEGIN
    EXECUTE format('UPDATE %s SET %I = %I + 1 RETURNING %I', countertable, countercolumn, countercolumn, countercolumn) INTO next_value;
    RETURN next_value;
END;
$$ LANGUAGE plpgsql;

COMMENT ON get_next_id(countername regclass) IS 'Increment and return value from integer column $2 in table $1';

Использование:

INSERT INTO dummy(id, blah) 
VALUES ( get_next_id('thetable_id_counter','last_id'), 42 );

Обратите внимание, что, когда одна открытая транзакция получила идентификатор, все другие транзакции, которые пытаются вызвать get_next_id, будут блокироваться до тех пор, пока 1-я транзакция не завершится или не откатится. Это неизбежно и для пропусков идентификаторов, и это предусмотрено.

Если вы хотите сохранить в таблице несколько счетчиков для разных целей, просто добавьте параметр в вышеуказанную функцию, добавьте столбец в таблицу счетчиков и добавьте предложение WHERE к UPDATE, соответствующее параметру в добавленный столбец. Таким образом, вы можете иметь несколько независимых счетчиков. не просто добавьте дополнительные столбцы для новых счетчиков.

Эта функция не использует повторно удаленные идентификаторы, она просто позволяет избежать пропусков.

Для повторного использования идентификаторов я советую ... не повторное использование идентификаторов.

Если вам действительно необходимо, вы можете сделать это, добавив триггер ON INSERT OR UPDATE OR DELETE к таблице интересов, который добавляет удаленные идентификаторы в боковую таблицу свободного списка и удаляет их из таблицы свободного списка, когда они INSERT изд. Считайте UPDATE DELETE, а затем INSERT. Теперь измените приведенную выше функцию генерации идентификатора так, чтобы она выполняла SELECT free_id INTO next_value FROM free_ids FOR UPDATE LIMIT 1 и, если найдено, DELETE с этой строкой. IF NOT FOUND получает новый идентификатор из таблицы генератора как обычно. Вот непроверенное расширение предыдущей функции для поддержки повторного использования:

CREATE OR REPLACE FUNCTION get_next_id_reuse(countertable regclass, countercolumn text, freelisttable regclass, freelistcolumn text) RETURNS integer AS $$
DECLARE
    next_value integer;
BEGIN
    EXECUTE format('SELECT %I FROM %s FOR UPDATE LIMIT 1', freelistcolumn, freelisttable) INTO next_value;
    IF next_value IS NOT NULL THEN
        EXECUTE format('DELETE FROM %s WHERE %I = %L', freelisttable, freelistcolumn, next_value);
    ELSE
        EXECUTE format('UPDATE %s SET %I = %I + 1 RETURNING %I', countertable, countercolumn, countercolumn, countercolumn) INTO next_value;
    END IF;
    RETURN next_value;
END;
$$ LANGUAGE plpgsql;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...