带有 SEQUENCE 的 PostgreSQL "duplicate key violation"
PostgreSQL "duplicate key violation" with SEQUENCE
[问题已解决。请参阅下面的答案。]
我刚刚遇到了一系列“重复键值违反唯一约束”的错误,系统已经运行了几个月。我无法确定它们发生的原因。
这里是错误:
org.springframework.dao.DuplicateKeyException: PreparedStatementCallback;
SQL [
INSERT INTO transaction_item
(transaction_group_id, transaction_type, start_time, end_time) VALUES
(?, ?::transaction_type_enum, ?, ?)
];
ERROR: duplicate key value violates unique constraint "transaction_item_pkey"
Detail: Key (transaction_id)=(67109) already exists.;
这里是相关SEQUENCE的定义和TABLE:
CREATE SEQUENCE transaction_id_seq AS bigint;
CREATE TABLE transaction_item (
transaction_id bigint PRIMARY KEY DEFAULT NEXTVAL('transaction_id_seq'),
transaction_group_id bigint NOT NULL,
transaction_type transaction_type_enum NOT NULL,
start_time timestamp NOT NULL,
end_time timestamp NOT NULL
);
这里是用于插入 table 的唯一 SQL 语句:
INSERT INTO transaction_item
(transaction_group_id, transaction_type, start_time, end_time) VALUES
(:transaction_group_id, :transaction_type::transaction_type_enum, :start_time, :end_time)
如您所见,我并没有明确尝试设置 transaction_id 的值。我已经为列定义定义了一个默认值,并使用它从 SEQUENCE 中获取一个值。
我一直觉得上面的方法是安全的,即使是在高并发的情况下使用。一个 SEQUENCE 永远不应该 return 相同的值两次,对吗?
如果能帮助我理解发生这种情况的原因以及解决方法,我将不胜感激。谢谢!
我找到了这个问题的原因。
几个月前(在开发此系统期间)发现了一个问题,这意味着有必要从数据库中清除任何现有的测试数据。我对所有 TABLES 使用 DELETE FROM 语句,对所有 SEQUENCES 使用 ALTER ... RESTART 语句。这些语句已添加到 Liquibase 配置中,以便在新代码启动期间执行。从当时检查日志来看,系统的一个实例似乎在迁移时仍然是 运行。事情发生了:系统的新实例删除了 TRANSACTION_ITEM table 中的所有数据,仍然是 运行 的实例然后向 table 添加了更多数据,然后新实例重新启动了用于插入这些记录的 SEQUENCE。所以昨天,当我收到重复键违规时,是因为 SEQUENCE 最终达到了与 TRANSACTION_ITEM 记录对应的 ID 值,这些记录是在数据库清除和迁移发生时由 still-运行 实例添加的。
说来话长,但现在一切都说得通了。感谢那些对此问题发表评论的人。
[问题已解决。请参阅下面的答案。]
我刚刚遇到了一系列“重复键值违反唯一约束”的错误,系统已经运行了几个月。我无法确定它们发生的原因。
这里是错误:
org.springframework.dao.DuplicateKeyException: PreparedStatementCallback;
SQL [
INSERT INTO transaction_item
(transaction_group_id, transaction_type, start_time, end_time) VALUES
(?, ?::transaction_type_enum, ?, ?)
];
ERROR: duplicate key value violates unique constraint "transaction_item_pkey"
Detail: Key (transaction_id)=(67109) already exists.;
这里是相关SEQUENCE的定义和TABLE:
CREATE SEQUENCE transaction_id_seq AS bigint;
CREATE TABLE transaction_item (
transaction_id bigint PRIMARY KEY DEFAULT NEXTVAL('transaction_id_seq'),
transaction_group_id bigint NOT NULL,
transaction_type transaction_type_enum NOT NULL,
start_time timestamp NOT NULL,
end_time timestamp NOT NULL
);
这里是用于插入 table 的唯一 SQL 语句:
INSERT INTO transaction_item
(transaction_group_id, transaction_type, start_time, end_time) VALUES
(:transaction_group_id, :transaction_type::transaction_type_enum, :start_time, :end_time)
如您所见,我并没有明确尝试设置 transaction_id 的值。我已经为列定义定义了一个默认值,并使用它从 SEQUENCE 中获取一个值。
我一直觉得上面的方法是安全的,即使是在高并发的情况下使用。一个 SEQUENCE 永远不应该 return 相同的值两次,对吗?
如果能帮助我理解发生这种情况的原因以及解决方法,我将不胜感激。谢谢!
我找到了这个问题的原因。
几个月前(在开发此系统期间)发现了一个问题,这意味着有必要从数据库中清除任何现有的测试数据。我对所有 TABLES 使用 DELETE FROM 语句,对所有 SEQUENCES 使用 ALTER ... RESTART 语句。这些语句已添加到 Liquibase 配置中,以便在新代码启动期间执行。从当时检查日志来看,系统的一个实例似乎在迁移时仍然是 运行。事情发生了:系统的新实例删除了 TRANSACTION_ITEM table 中的所有数据,仍然是 运行 的实例然后向 table 添加了更多数据,然后新实例重新启动了用于插入这些记录的 SEQUENCE。所以昨天,当我收到重复键违规时,是因为 SEQUENCE 最终达到了与 TRANSACTION_ITEM 记录对应的 ID 值,这些记录是在数据库清除和迁移发生时由 still-运行 实例添加的。
说来话长,但现在一切都说得通了。感谢那些对此问题发表评论的人。