Какие проблемы могут возникнуть при использовании строкового типа для идентификаторов в базе данных?
Что проверяют
Понимаете ли вы, во что обходится строковый ключ на уровне индексов, внешних ключей и самого Python-кода. Следом обычно спрашивают, когда строка всё-таки оправдана и чем её заменить, если ключ нужно генерировать вне базы.
Как отвечать
Начните с того, что первичный ключ живёт не в одной колонке: он повторяется в каждом индексе и в каждой таблице, которая на него ссылается. Отсюда проблемы:
- размер:
bigintзанимает 8 байт,uuid16, а тот же UUID строкой вvarcharоколо 37 байт. Индексы раздуваются, в память помещается меньше страниц; - сравнение: строки сравниваются с учётом collation, это дороже сравнения чисел, и порядок зависит от настроек базы;
- сортировка:
"10"оказывается раньше"9", а"007"и"7"считаются разными ключами; - регистр и пробелы:
"ABC"и"abc "проходят уникальный индекс как разные значения, если их не нормализовать; - несовпадение типов: сравнение
varcharс числом или сuuidзаставляет базу приводить тип, и индекс может не использоваться.
Отдельно про случайные UUIDv4: вставка в случайное место B-tree даёт расщепление страниц и фрагментацию. Если ключ генерирует приложение, берите UUIDv7, он растёт по времени: в PostgreSQL 18 есть функция uuidv7(), в Python 3.14 появился uuid.uuid7(). Храните его нативным типом: UUIDField в Django на PostgreSQL ложится в колонку uuid, в SQLAlchemy 2.x есть тип Uuid.
Строка оправдана для естественных ключей с жёстким форматом (коды валют, стран) и для внешних идентификаторов, которые вы не контролируете. Даже тогда удобно оставить первичным ключом bigint, а внешний ID держать в отдельной колонке с уникальным индексом.
Где спотыкаются
Говорят «строки просто медленнее» и не могут объяснить, где именно: в размере индексов, в collation, в каскаде внешних ключей.
Предлагают UUIDv4 как бесплатную замену. На большой таблице с интенсивной вставкой он заметно проигрывает последовательному ключу.
Хранят UUID в CharField и в Python сравнивают объект uuid.UUID со строкой: такое сравнение всегда даёт False, и поиск молча ничего не находит.