Mic*_*and 7 mysql sql postgresql standards transaction-isolation
当使用SERIALIZABLE事务来实现仅在数据库不存在的情况下将值插入到数据库中的模式时,我观察到 MySQL 和 PostgreSQL 在SERIALIZABLE隔离级别的定义中存在显着差异。
考虑下表:
CREATE TABLE person (
person_id INTEGER PRIMARY KEY AUTO_INCREMENT,
name VARCHAR NOT NULL
);
Run Code Online (Sandbox Code Playgroud)
以及以下插入代码,在两个连接上同时运行:
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
START TRANSACTION;
SELECT person_id FROM person WHERE name = 'Bob Ross';
-- sync point: both transactions should run through here before proceeding to
-- demonstrate the effect
-- 0 results, so we will insert
INSERT INTO person (name) VALUES ('Bob Ross');
SELECT last_insert_id();
COMMIT;
Run Code Online (Sandbox Code Playgroud)
在 PostgreSQL 中(经过适当的 SQL 翻译后),效果如我所料:只有一个事务可以成功提交。这与我对 PostgreSQL 所描述的 SERIALIZABLE 以及引用自 ANSI 标准的其他来源的理解一致:存在会产生相同效果的事务的串行执行。这两个事务没有串行执行,搜索返回 0 结果,然后添加条目。
在 MySQL 5.7 中,两个事务都成功并且表中有 2 个“Bob Ross”条目。MySQL 文档SERIALIZABLE在禁止脏读、不可重复读和幻读方面进行了定义;它没有提到串行执行的存在。
由于其保守的锁定策略,SQLite 也正确地防止了双重插入,至少在其默认模式下是这样。
我的问题:MySQL 在这种情况下的行为是正确的,还是因为允许这些事务都成功而违反了 SQL 标准? 我怀疑答案可能取决于“效果”的定义——SELECT为了两个具有相同效果的串行执行的目的,从第一次观察空结果集是否作为“效果”?
一些其他评论有助于确定这个问题的范围:
ON CONFLICT IGNORE,然后执行选择来在 MySQL 中实现所需的行为。我试图理解为什么等效的标准 SQL 在两个引擎中没有表现出相同的行为。name字段上设置一个独特的约束来修复它,无论如何这可以说是一个更好的数据模型。但核心问题仍然存在:为什么这些交易都能成功?SQL 标准在第 4.35.4 章中介绍了 SQL 事务的隔离级别中说(强调我的):
\n\n\n在隔离级别 SERIALIZABLE 下执行并发 SQL 事务保证是可序列化的。可序列化执行被定义为并发执行 SQL 事务的操作的执行,其产生与那些相同 SQL 事务的某些串行执行相同的效果。串行执行是指每个 SQL 事务在下一个 SQL 事务开始之前执行完成。
\n
再往下看,它继续使问题变得混乱:
\n\n\n隔离级别指定并发 SQL 事务执行期间可能发生的现象类型。可能出现以下现象:
\n[跳过P1 (\xe2\x80\x9c脏读\xe2\x80\x9d)、P2 (\xe2\x80\x9c不可重复读\xe2\x80\x9d)和P3 (\xe2\x80\x9cPhantom\xe2 \x80\x9d) ]
\n四个隔离级别保证每个 SQL 事务都将完全执行或根本不执行,并且不会丢失任何更新。现象P1、P2和P3的隔离级别不同 ,隔离级别是不同的。表 8,\xe2\x80\x9cSQL 事务隔离级别和三种现象\xe2\x80\x9d 指定对于给定隔离级别可能和不可能的现象。
\n
\n\nRun Code Online (Sandbox Code Playgroud)\n+------------------+--------------+--------------+--------------+ \n| Level | P1 | P2 | P3 |\n+------------------+--------------+--------------+--------------+\n| READ UNCOMMITTED | Possible | Possible | Possible |\n+------------------+--------------+--------------+--------------+\n| READ COMMITTED | Not Possible | Possible | Possible |\n+------------------+--------------+--------------+--------------+\n| REPEATABLE READ | Not Possible | Not Possible | Possible |\n+------------------+--------------+--------------+--------------+\n| SERIALIZABLE | Not Possible | Not Possible | Not Possible |\n+------------------+--------------+--------------+--------------+\n注意 53 \xe2\x80\x94 对于在 SERIALIZABLE 隔离级别执行的 SQL 事务,排除这些现象是要求此类事务可串行化的结果。
\n
这种措辞产生了不幸的后果,即许多实现者认为排除脏读、不可重复读和幻读就足以正确实现SERIALIZABLE隔离级别,尽管注释澄清这不是定义,而是以下结果:定义。
所以我认为 MySQL 是错误的,但它并不是唯一的:Oracle 数据库解释SERIALIZABLE以同样的方式解释。