PostgreSQL与Oracle默认事务管理

时间:2016-08-11 18:59:37

标签: oracle postgresql transactions

在PostgreSQL中,如果您在事务中遇到错误(例如,当您的insert语句违反了唯一约束时),整个事务将被中止,您无法提交它并且不会插入任何行:

database=# begin;
BEGIN
database=# insert into table (id, something) values ('1','whatever');
INSERT 0 1
database=# insert into table (id, something) values ('1','whatever');
ERROR:  duplicate key value violates unique constraint "table_id_key"
Key (id)=(1) already exists.
database=# insert into table (id, something) values ('2','whatever');
ERROR:  current transaction is aborted, commands ignored until end of transaction block
database=# rollback;
database=# select * from table;
id   | something  | 
-----+------------+

(0 rows)

您可以通过将 ON_ERROR_ROLLBACK 设置为" on"来更改它。或者" interactive",之后你可以做多个插入而忽略错误,提交并且只在事务结束后在表中成功插入行。

database=# \set ON_ERROR_ROLLBACK interactive

在Oracle中,这是默认的事务管理行为,这让我感到惊讶。这不完全是违反直觉和危险的吗?

当我开始一个交易时,我想确保所有的陈述都是成功的。如果我的多个插入包含某种对象或数据结构怎么办?我最终完全没有意识到我的数据库中的数据状态,应该在提交后检查它。 如果其中一个插入失败,我想确保在第一个错误之后回滚或甚至不评估其他插入,这正是它在PostgreSQL中的完成方式。

为什么Oracle将这种事务管理方式作为默认方式,为什么它被认为是良好的做法?

例如,一些随机的人here in comments

  

这是一个非常简洁的功能。

     

我不明白这一点:"通常情况下,你所犯的任何错误都会   抛出异常并将当前事务标记为   中止。这是理智和预期的行为......"

     

不,它真的不是。 Oracle不会以这种方式工作,MySQL也不会。一世   没有使用MSSQL或DB2的经验,但我会给他们打赌一美元   不要这样工作。语法没有直观的原因   错误或任何其他错误应该中止事务。   我只能假设Postgres有一些限制   需要此行为的内容,或者它符合某些模糊不清的内容   其他人明智地忽略的SQL标准的一部分。有' S   当然没有API / UX的原因,为什么它应该以这种方式工作。

     

我们真的不应该为我们开发的任何变通办法感到骄傲   对于这种病态行为。它就像IT斯德哥尔摩综合症。

它是否违反了交易的定义?

  

交易提供"全有或全无"命题,说明   在数据库中执行的每个工作单元必须在其中完成   完全或没有任何影响。

1 个答案:

答案 0 :(得分:3)

我同意你的看法。我认为不中止整个tx是错误的。但是人们已经习惯了,所以他们认为这是合理和正确的。就像使用MySQL的人认为DBMS应该接受0000-00-00作为日期,或者使用Oracle的人期望'' IS NULL

语法错误与其他内容之间存在明显区别的想法存在缺陷。

如果我写

BEGIN;

CREATE TABLE new_customers (...);

INSET INTO new_customers (...)
SELECT ... FROM customers;

DROP TABLE customers;

COMMIT;

我不在乎这是一个错字导致语法错误导致我丢失数据。我关心事务没有成功执行所有语句但仍然提交。

在任何行实际写入语句之前允许在PostgreSQL中进行软回滚在技术上是可行的 - 可能在我们进入执行程序之前。因此,解析和参数绑定阶段中的失败可能允许tx不被中止。我们有一个可以用来清理的语句记忆上下文。

但是,一旦语句开始更改行,它就会在磁盘上执行,其事务ID与tx中的先前语句相同。所以你不能回滚而不回滚整个tx。要允许语句回滚,Pg需要分配新的子事务ID。这需要资源。您可以在需要时使用SAVEPOINT显式地执行此操作,并在内部执行psql正在执行的操作。从理论上讲,我们可以允许服务器为每个语句隐式执行此操作以实现语句回滚,只是性能成本。但是我怀疑实现这一点的任何补丁都会得到承诺,至少在没有大量争论的情况下,因为大多数PostgreSQL团队(IMO合理地)都不喜欢“哎呀,但是我们会继续这样做”但是我们会继续“交易语义。” / p>