och*_*les 5 postgresql transaction serialization
我很难弄清楚如何准确实现“如果未找到则插入”功能。考虑以下。
我们有一个名为artist
2 列的表,(name, id)
其中name
是唯一id
键,是串行主键。这是一个人为的例子,但它说明了我的问题:
SESSION A SESSION B
1. SELECT id FROM artist
WHERE name = 'Bob';
2. INSERT INTO artist (name)
VALUES ('Bob')
3. INSERT INTO artist (name)
VALUES ('Bob')
4. code that users 'Bob'
(e.g., a FK to Bob's ID)
5. ??? Bob already exists, but we
can't find it
4. COMMIT
Run Code Online (Sandbox Code Playgroud)
会话 B 开始尝试找到一个artist
叫 Bob 的人,但失败了。但是,会话 A 然后创建了 Bob。会话 B 尝试插入名为 Bob 的艺术家,但由于违反主键而失败。但这是我不明白的一点——如果我将操作 3 更改artist
为表上的选择仍然是空的!这是因为我使用了可序列化的隔离级别,但是我该如何处理这种情况呢?
似乎我唯一的选择是中止整个交易并重试。如果是这种情况,我是否应该抛出我自己的“无法序列化”异常,指示应用程序应该重试?我已经想要在 plpgsql 函数中使用这个“查找或插入” INSERT
,如果失败,SELECT
但似乎不可能找到冲突的行......
这是一个常见问题解答。如果您搜索ON DUPLICATE KEY UPDATE
(MySQL 语法)、MERGE
(SQL 标准语法)或UPSERT
. 出奇的难。
我见过的最好的文章是Depesz 的“为什么 upsert 如此复杂”。还有 SO 问题插入,在重复更新 (postgresql)上有建议但缺乏对问题的解释和讨论。
简短的回答是,是的:
似乎我唯一的选择是中止整个交易并重试。
使用SERIALIZABLE
事务时,您只需要在它们失败时重新发出它们。他们会的。通过设计 - 由于大大改进了冲突检测,因此在 Pg 9.1 及更高版本上更频繁。类似 Upsert 的操作是非常高的冲突,所以你可能会重试很多次。如果您可以在READ COMMITTED
事务中进行更新插入,它会有所帮助,但您仍然应该准备重试,因为存在一些不可避免的竞争条件。
当您插入冲突行时,让事务因唯一违规而失败。如果您unique_violation
从事务中得到 SQLSTATE 23505失败并且您知道您正在尝试更新插入,请重试。如果您收到 SQLSTATE 40001,serialization_failure
您也应该重试。
您基本上不能在 PL/PgSQL 函数中进行重试(没有像 dblink 这样的脏黑客),它必须是应用程序端。如果 PostgreSQL 有具有自治事务的存储过程,那么它是可能的,但它没有。在READ COMMITTED
模式中,您可以检查相冲突开始以来的交易进行的插入,但不能在调用启动的PL / pgSQL函数的声明之后,所以即使是在READ COMMITTED
你的方法根本行不通“与选择检测冲突”。
阅读 depesz 的文章以获得更好、更详细的解释。
归档时间: |
|
查看次数: |
3132 次 |
最近记录: |