Чому Postgres генерує вже використане значення ПК?


22

Я використовую Django, і раз у раз я отримую цю помилку:

IntegrityError: значення дублюючого ключа порушує унікальне обмеження "myapp_mymodel_pkey" ДЕТАЛІ
: Key (id) = (1) вже існує.

У моїй базі даних Postgres насправді є об'єкт myapp_mymodel з первинним ключем 1.

Чому Postgres намагається знову використовувати цей первинний ключ? Або це, швидше за все, причиною цього є моя заявка (або ОРМ Джанго)?

Ця проблема виникала ще 3 рази поспіль. Те , що я виявив, що , коли це дійсно відбувається це відбувається один або кілька разів поспіль для даної таблиці, а потім не раз. Здається, це трапляється з кожним столом, перш ніж він повністю припиняється днями, трапляється принаймні хвилину або близько того за столом, коли він відбувається, і відбувається лише з перервами (не всі таблиці відразу).

Той факт, що ця помилка є настільки переривчастою (траплялася лише 3 або більше разів за 2 тижні - ніякого іншого навантаження на БД, тільки я перевіряю свою програму), це те, що мене насторожено ставиться до проблеми низького рівня.


Django конкретно заявляє, що первинний ключ генерується СУБД, якщо не вказано - тепер я не знаю, що робив @orokusaky у своєму пітон-коді, але я опинився на цій сторінці, оскільки я впевнений, що у мене немає коду намагаючись використовувати певний первинний ключ, і я ніколи не бачив СУБД, що намагалася використовувати неправильний.
mccc

Відповіді:


34

PostgreSQL не намагатиметься вставити повторювані значення самостійно, це саме ви (ваша програма, включена ORM).

Це може бути або послідовність подачі значень PK, встановленого в неправильному положенні, і таблиця, яка вже містить значення, рівне його nextval()- або просто те, що ваша програма робить неправильну справу. Перший легко виправити:

SELECT setval('your_sequence_name', (SELECT max(id) FROM your_table));

Другий означає налагодження.

Django (або будь-який інший популярний фреймворк) не скидає послідовності самостійно - інакше у нас будуть подібні запитання через день.


Чи варто зауважувати (також виходячи з відповіді @ andi тут) про різні рівні ізоляції? Наприклад, якщо другий запит надходить до завершення першого, чи можливо, враховуючи сценарій, коли я не використовую транзакції, вставити запис, який призводить до отримання max(id)до завершення першого запиту, а потім у результаті виникнення обох той же результат?
orokusaki

7

Ви, швидше за все, пов'язуєте вставити рядок у таблицю, для якої значення послідовності послідовних стовпців не оновлюється.

Розглянемо наступний стовпчик таблиці, який є первинним ключем, визначеним Django ORM для постгресів

id serial NOT NULL

Чиє значення за замовчуванням встановлено

nextval('table_name_id_seq'::regclass)

Послідовність оцінюється лише тоді, коли поле id встановлено як порожнє. Але це проблема, якщо в таблиці вже є записи.

Питання: чому ці попередні записи не викликали оновлення послідовності? Це тому, що значення id явно було передбачено для всіх попередніх записів.

У моєму випадку ці початкові записи завантажувались із світильників через міграції.

Ця проблема також може бути складною через користувацькі записи з випадковим значенням ПК.

Скажіть, наприклад. У вашу таблицю 10 записів. Ви робите чіткий запис із ПК = 15. Наступні чотири вставки через код спрацювали б чудово, але 5-й створив би виключення.

DETAIL: Key (id)=(15) already exists.

Дякую за цю публікацію Я давно налагоджую подібний випадок. Дуже рідко це траплялося. Виявилося, що конкретна "ручна" функція адміністратора може вставляти ідентифікатори самостійно, залишаючи лічильник ідентичності зі старим значенням. Це справжня небезпека із "ЗАГАЛЕНОЮ ЗА ЗАВДАННЯМ ЯК ІДЕНТИЧНОСТІ". Я подумаю двічі, перш ніж використовувати "ЗА ЗАМОВЛЕННЮ" замість "ЗАВЖДИ" наступного разу, коли визначу стовпець посвідчення.
Майкл

4

Я опинився тут з дуже такою самою помилкою, яка траплялася рідко, і важко було відстежити, бо я шукав її не там, де повинен.

Помилкою було повторення JS, яке двічі робило POST на сервері! Тому іноді варто придивитися не лише до поглядів та форм вашого джанго (чи будь-яких інших веб-рамок), а й до того, що відбувається на дуже передній стороні.


1

Так дивна річ. У моєму випадку щось, мабуть, неправильно під час завантаження даних під час міграції. Я додав порожню міграцію і написав рядки, щоб додати деякі початкові дані, 6 записів у моєму випадку.

db_alias = schema_editor.connection.alias
bulk = []
for item in items:
    bulk.append(MyModel(
        id=item[0],
        value=item[1],
        slug=item[2],
        name=item[3],
    ))

MyModel.objects.using(db_alias).bulk_create(bulk)

Потім в панелі адміністратора я спробував додати новий елемент і отримав:

Перша спроба:

DETAIL:  Key (id)=(1) already exists.

Пізніші спроби:

DETAIL:  Key (id)=(2) already exists.
DETAIL:  Key (id)=(3) already exists.
DETAIL:  Key (id)=(4) already exists.
DETAIL:  Key (id)=(5) already exists.
DETAIL:  Key (id)=(6) already exists.

І, нарешті, 7-е і часом все успішно

Тому я кажу, що, можливо, щось пов’язане з bulk_create, коли я завантажував туди 6 предметів. Це може бути щось подібне у вашому проекті Django.

Django 1.9 PostgreSQL 9.3.14

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.