Dark mode switch icon Light mode switch icon

Тип integer в колонке id как бомба замедленного действия

• 1 минут чтобы прочитать

Если вы работаете с Legacy проектом, то загляните в структуру ваших таблиц - возможно некоторые из них используют integer в качестве id поля, и это очень плохо.

В чём проблема

Тип integer имеет лимиты от -2147483648 до +2147483647 (от 1 до 2147483647 для serial) в PostgresQL. К этому лимиту довольно легко подойти, особенно если система была создана уже значительно время назад.

Особенность этой проблемы:

  1. она не будет тригериться деплоем или другими действиями с кодом
  2. она не проявится от аномальной нагрузки
  3. может затронуть очень разный функционал

Как проверить

Если у вас небольшой проект и таблиц немного, то можно руками и глазами проверить каждую таблицу. Однако это плохо масштабируется.

Можно проверить размерность всех id колонок во всех таблицах, однако в разное время даже в одном проекте могут использоваться разные конвенции именования, поэтому часть таблиц могут иметь колонки вроде product_id или rid и т.п.

Ещё один более масштабируемый вариант - проверить текущие значения в последовательностях используемых для автоинкрементных колонок. Для этого подойдёт такой запрос:

SELECT c.relname, nextval(c.relname::regclass) AS seq_val
FROM pg_class c
WHERE c.relkind = 'S'
  AND  nextval(c.relname::regclass) > 1000000000
ORDER BY seq_val desc;

В результате получим список последовательностей превысивших 1 млрд., отсортированных по убывающей. Верхний лимит integer как мы помним больше 2 млрд. поэтому значение в 1 млрд. выглядит как то, с чего стоит начать в первую очередь. По названию sequence можно довольно легко определить какая таблица её использует.

Не забываем про таблицы ссылающиеся на изменённые id в качестве внешних ключей для связи многие-к-одному или многие-ко-многим. Если foreign key constraint прописаны для таких таблиц, то их можно найти следующим запросом (здесь мы ищем внешние ключи ссылающиеся на my_table_name(id)):

SELECT la.attrelid::regclass AS referencing_table,
       la.attname AS referencing_column
FROM pg_constraint AS c
         JOIN pg_index AS i
              ON i.indexrelid = c.conindid
         JOIN pg_attribute AS la
              ON la.attrelid = c.conrelid
                  AND la.attnum = c.conkey[1]
         JOIN pg_attribute AS ra
              ON ra.attrelid = c.confrelid
                  AND ra.attnum = c.confkey[1]
WHERE c.confrelid = 'my_table_name'::regclass
  AND c.contype = 'f'
  AND ra.attname = 'id'
  AND cardinality(c.confkey) = 1;

Однако foreign key constraint есть не всегда (а порой его создание может быть вообще невозможным), поэтому стоит принять это во внимание - на этом автоматизация заканчивается и дальше придётся проверять руками и глазами.

На что заменить

Простым решением для текущего состояния будет перевести тип колонки из int в bigint - запаса прочности этого типа данных должно хватить.

Однако не стоит считать что bigint это серебряная пуля при выборе типа id колонки в новых таблицах - всё конечно же будет зависеть от ваших сценариев использования и предназначения таблицы, так что ответ здесь как всегда - it depends.

Впервые опубликовано от Эдуард Сухарев