什么可以允许使用相同的复合键将多个条目输入数据库?
What could allow several entries into the database with identical composite keys?
SELECT user_id FROM contacts WHERE phone = "+18888576309";
user_id | phone | created_at
--------------+---------------+----------------------------
db0db85f2331 | +18888576309 | 2022-03-22 23:28:04.308612
db0db85f2331 | +18888576309 | 2022-03-22 23:28:04.310283
db0db85f2331 | +18888576309 | 2022-03-22 23:28:04.311891
这看起来并不奇怪,直到您意识到模型是这样定义的:
class Contact(db.Model):
__tablename__ = 'contacts'
user_id = db.Column(VARCHAR(16), primary_key=True)
phone = db.Column(VARCHAR(64), primary_key=True)
created_at = db.Column(DateTime)
def __init__(self, user_id, phone):
self.user_id = user_id
self.contact_phone = phone
self.created_at = datetime.datetime.utcnow()
注意:这里定义了两个主键(making,afaik,复合键)。他们是 user_id
和 phone
.
我已经尝试了比我愿意承认的时间更长的时间来寻找这可能发生的来源,但互联网似乎没有任何关于这种可能性的信息(实际上有几篇文章说这个是不可能的!)。唯一不寻常的事情,我相信这是问题的根源(尽管这纯粹是一种直觉)是保存这些对象的方法使用 Session.bulk_save_objects()。到目前为止我有:
- 检查了所有相关的迁移;任何改变
contacts
table 的事情都发生在 2022-03-22 之前
- 检查了定义
Contact
和处理联系人创建的文件的文件历史记录——自 2022 年 3 月 22 日以来一直没有变化
- 尝试通过多种方式在本地重现此问题(包括使用
multiprocessing
尝试同时调用同一对象列表上的 Session.bulk_save_objects()
)
- 尽我所能阅读 SQLAlchemy 文档中关于
Session.bulk_save_objects()
这种事情怎么会发生?我一直认为,由于某些基本的数据库逻辑,具有相同主键的行不可能存在,而且可能发生这种情况的唯一方法是,如果两个不同的进程同时向数据库添加相同的行。
There are two primary keys defined here (making, afaik, a composite key).
如果(user_id, phone)
上确实有PK,那么你的输出就没有任何意义。 UNIQUE
索引(支持 PRIMARY KEY
和 UNIQUE
约束)始终强制执行。
首先要确保你没有找错树。是否有多个名为“联系人”或“联系人”的 table?在多个模式中?
检查:
SELECT oid, relnamespace::regnamespace AS schema, relname, relkind
FROM pg_class
WHERE relname ILIKE 'contacts';
参见:
- Are PostgreSQL column names case-sensitive?
- How to check if a table exists in a given schema
然后用 \d contacts
检查 psql 中实际的 table 定义 - 或者任何实际的,正确的 fully-qualified tablename 是。
如果这不能消除迷雾(就像它可能会的那样),您一定是在查看损坏的索引。尽快用 REINDEX
修复:
REINDEX TABLE contacts;
或者,至少,只有一个索引:
REINDEX INDEX contacts_pkey; -- use actual name
或者,更激进地,使用 VACUUM FULL
(从头重写 table 和所有索引):
VACUUM FULL contacts;
两者都阻止并发访问。
我自己从未见过损坏的索引,但它确实发生了 - 特别是在旧版本 and/or 有故障的硬件上。最好查明原因...
SELECT user_id FROM contacts WHERE phone = "+18888576309";
user_id | phone | created_at
--------------+---------------+----------------------------
db0db85f2331 | +18888576309 | 2022-03-22 23:28:04.308612
db0db85f2331 | +18888576309 | 2022-03-22 23:28:04.310283
db0db85f2331 | +18888576309 | 2022-03-22 23:28:04.311891
这看起来并不奇怪,直到您意识到模型是这样定义的:
class Contact(db.Model):
__tablename__ = 'contacts'
user_id = db.Column(VARCHAR(16), primary_key=True)
phone = db.Column(VARCHAR(64), primary_key=True)
created_at = db.Column(DateTime)
def __init__(self, user_id, phone):
self.user_id = user_id
self.contact_phone = phone
self.created_at = datetime.datetime.utcnow()
注意:这里定义了两个主键(making,afaik,复合键)。他们是 user_id
和 phone
.
我已经尝试了比我愿意承认的时间更长的时间来寻找这可能发生的来源,但互联网似乎没有任何关于这种可能性的信息(实际上有几篇文章说这个是不可能的!)。唯一不寻常的事情,我相信这是问题的根源(尽管这纯粹是一种直觉)是保存这些对象的方法使用 Session.bulk_save_objects()。到目前为止我有:
- 检查了所有相关的迁移;任何改变
contacts
table 的事情都发生在 2022-03-22 之前
- 检查了定义
Contact
和处理联系人创建的文件的文件历史记录——自 2022 年 3 月 22 日以来一直没有变化 - 尝试通过多种方式在本地重现此问题(包括使用
multiprocessing
尝试同时调用同一对象列表上的Session.bulk_save_objects()
) - 尽我所能阅读 SQLAlchemy 文档中关于
Session.bulk_save_objects()
这种事情怎么会发生?我一直认为,由于某些基本的数据库逻辑,具有相同主键的行不可能存在,而且可能发生这种情况的唯一方法是,如果两个不同的进程同时向数据库添加相同的行。
There are two primary keys defined here (making, afaik, a composite key).
如果(user_id, phone)
上确实有PK,那么你的输出就没有任何意义。 UNIQUE
索引(支持 PRIMARY KEY
和 UNIQUE
约束)始终强制执行。
首先要确保你没有找错树。是否有多个名为“联系人”或“联系人”的 table?在多个模式中?
检查:
SELECT oid, relnamespace::regnamespace AS schema, relname, relkind
FROM pg_class
WHERE relname ILIKE 'contacts';
参见:
- Are PostgreSQL column names case-sensitive?
- How to check if a table exists in a given schema
然后用 \d contacts
检查 psql 中实际的 table 定义 - 或者任何实际的,正确的 fully-qualified tablename 是。
如果这不能消除迷雾(就像它可能会的那样),您一定是在查看损坏的索引。尽快用 REINDEX
修复:
REINDEX TABLE contacts;
或者,至少,只有一个索引:
REINDEX INDEX contacts_pkey; -- use actual name
或者,更激进地,使用 VACUUM FULL
(从头重写 table 和所有索引):
VACUUM FULL contacts;
两者都阻止并发访问。
我自己从未见过损坏的索引,但它确实发生了 - 特别是在旧版本 and/or 有故障的硬件上。最好查明原因...