Postgresがすでに使用されているPK値を生成するのはなぜですか?


19

私はDjangoを使用していますが、時々このエラーが発生します:

IntegrityError:重複キー値が一意の制約 "myapp_mymodel_pkey"に違反しています
詳細:キー(id)=(1)は既に存在します。

実際、私のPostgresデータベースには、主キーが1のmyapp_mymodelオブジェクトがあります。

Postgresが再びその主キーを使用しようとするのはなぜですか?または、これはおそらくアプリケーション(またはDjangoのORM)がこれを引き起こしているのでしょうか?

この問題は、ちょうど3回続けて発生しました。私が見つけたのは、それ発生した場合、特定のテーブルの行で1回以上発生し、その後は発生しないことです。これは、数日間完全に停止する前にすべてのテーブルで発生し、発生したテーブルごとに少なくとも1分程度発生し、断続的にのみ発生するようです(すぐにすべてのテーブルが発生するわけではありません)。

このエラーが非常に断続的である(2週間で3回程度しか発生しなかった-DBに他の負荷がなく、アプリケーションをテストしているだけである)ため、低レベルの問題に非常に警戒しています。


Django は特に指定しない限り主キーはDBMSによって生成されると述べています-今、彼のPythonコードで@orokusakyが何をしていたのかわかりませんが、コードがないと確信しているため、このページに行きました特定の主キーを使用しようとして、間違ったキーを使用しようとしているDBMSを見たことはありません。
mccc

回答:


33

PostgreSQLは、重複する値を単独で挿入しようとはしません。あなた(あなたのアプリケーション、ORMを含む)がそうします。

それは、間違った位置に設定されたPKセットに値を供給するシーケンスと、すでにその値に等しい値を含んでいるテーブルのいずれか、nextval()または単にアプリケーションが間違ったことをすることです。最初のものは簡単に修正できます:

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

2番目はデバッグを意味します。

Django(または他の一般的なフレームワーク)は、それ自体でシーケンスをリセットしません。そうでなければ、1日おきに同様の質問があります。


さまざまな分離レベルについて(ここでの@andiの回答にも基づいて)注目に値しますか?たとえば、最初のクエリが完了する前に2番目のクエリが入った場合、トランザクションを使用していないシナリオではmax(id)、最初のクエリが完了する前に取得するレコードを挿入し、両方の結果を得ることができますか?同じ結果?
-orokusaki

5

ほとんどの場合、シリアルカラムシーケンス値が更新されていないテーブルに行を挿入します。

Django ORMによってpostgresで定義されている主キーであるテーブルの次の列を検討してください

id serial NOT NULL

デフォルト値が設定されている人

nextval('table_name_id_seq'::regclass)

シーケンスは、idフィールドが空白に設定されている場合にのみ評価されます。しかし、すでにテーブルにエントリがある場合、それは問題です。

質問はなぜそれらの以前のエントリがシーケンスの更新をトリガーしなかったのですか?これは、以前のすべてのエントリに対してid値が明示的に提供されたためです。

私の場合、これらの初期エントリは、移行によってフィクスチャからロードされました。

この問題は、ランダムなPK値を持つカスタムエントリを介してトリッキーになることもあります。

たとえば テーブルには10個のエントリがあります。PK = 15で明示的なエントリを作成します。コードを介した次の4つの挿入は完全に機能しますが、5番目の挿入では例外が発生します。

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

この投稿をありがとう。私はこのようなケースを長い間デバッグしてきました。まれにしか発生しませんでした。特定の「手動」管理機能が独自にIDを挿入し、IDカウンターに古い値を残すことが判明しました。これは、「デフォルトとしてIDとして生成された」という本当の危険です。次回ID列を定義するときは、「常に」ではなく「デフォルトで」を使用する前に2回考えます。
マイケル

4

私はここで非常に同じエラーで終わりましたが、これはめったに発生せず、追跡するのが困難でした。

障害は、サーバーへのPOSTを2回行っていたJSの繰り返しでした!そのため、Django(または他のWebフレームワーク)のビューとフォームだけでなく、最前面で何が起こるかを確認する価値がある場合もあります。


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日以降はすべて成功しています

そこで、6つのアイテムをロードしたときにbulk_createに関連するものがあると言っています。それはあなたのDjangoプロジェクトでも似たようなものである可能性があります。

Django 1.9 PostgreSQL 9.3.14

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.