为什么Postgres会生成一个已经使用的PK值?


21

我正在使用Django,偶尔我会收到此错误:

IntegrityError:重复的键值违反了唯一约束“ myapp_mymodel_pkey”
详细信息:键(id)=(1)已存在。

实际上,我的Postgres数据库确实有一个myapp_mymodel对象,其主键为1。

为什么Postgres会再次尝试使用该主键?或者,这很可能是我的应用程序(或Django的ORM)引起的吗?

刚刚,此问题连续发生了3次。我发现的是,对于给定的表,它确实发生了一次或多次,然后不再发生。它似乎在每个表完全停止运行几天之前就发生了,每个表至少发生一分钟左右才发生,并且只是间歇地发生(并非所有表都立即发生)。

这个错误是如此断断续续的事实(在2周内仅发生了3次左右-数据库上没有其他负载,只是我测试了我的应用程序)使我非常警惕低级问题。


Django 特别指出,除非指定,否则主键是由DBMS生成的-现在,我不知道@orokusaky在他的python代码中正在做什么,但是我最终进入此页面是因为我非常有信心自己没有代码尝试使用特定的主键,而我从未见过DBMS尝试使用错误的主键。
mccc 2015年

Answers:


34

PostgreSQL不会尝试自己插入重复的值,而是您(您的应用程序,包括ORM)来执行。

它既可以是将值输入到设置为错误位置的PK的序列,又可以是已经包含等于其值的表的序列nextval()-或仅仅是您的应用程序执行了错误的操作。第一个很容易解决:

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

第二个是调试。

Django(或任何其他流行的框架)不会自行重置序列-否则我们每隔一天就会遇到类似的问题。


是否值得注意(也基于此处的@andi回答)不同的隔离级别?例如,如果第二个查询在第一个查询完成之前进入,在我不使用事务的情况下,是否有可能插入一条记录,导致在max(id)第一个查询完成之前获取,然后导致两者同样的结果?
orokusaki

6

您很可能会在表中插入一行,而该表的串行列序列值不会更新。

考虑表中的以下列,这是Django ORM为postgres定义的主键

id serial NOT NULL

谁的默认值设置为

nextval('table_name_id_seq'::regclass)

仅当id字段设置为空白时,才评估序列。但这是问题,如果表中已经有条目。

问题是为什么那些较早的条目不触发序列更新?这是因为为所有先前的条目显式提供了id值。

在我的情况下,这些初始条目是通过迁移从夹具中加载的。

通过具有随机PK值的自定义条目,此问题也会变得棘手。

比如说。表中有10个条目。您使用PK = 15进行显式输入。接下来的四个通过代码插入将完全正常,但第5个插入将引发异常。

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

感谢您对这篇文章。我已经调试了很长时间这样的情况。它很少发生。事实证明,特定的“手动”管理功能可以自己插入ID,而使身份计数器具有旧值。这是“默认身份产生”的真正危险。在下次定义标识列时,在使用“按默认”而不是“总是”之前,我会三思。
迈克尔

4

我最终在这里遇到了同样的错误,这种错误很少发生,并且很难跟踪,因为我一直在寻找错误,而不是在应该的位置。

错误是JS重复,它两次对服务器执行POST!因此,有时值得不仅看一下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.

最后第七次按时都成功了

所以我说的是在我加载6个项目时,可能与bulk_create有关。这可能是您的Django项目中的类似原因。

Django 1.9 PostgreSQL 9.3.14

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.