MySQL 解释 SERIALIZABLE 比 PostgreSQL 更轻松。这是正确的吗?
MySQL interprets SERIALIZABLE less strenuously than PostgreSQL. Is it correct?
当使用 SERIALIZABLE
事务来实现仅在数据库不存在时才将值插入数据库的模式时,我观察到 MySQL 和 Postgre[=52= 之间存在显着差异] 在他们对 SERIALIZABLE
隔离级别的定义中。
考虑以下 table:
CREATE TABLE person (
person_id INTEGER PRIMARY KEY AUTO_INCREMENT,
name VARCHAR NOT NULL
);
以及以下插入代码,运行 同时在两个连接上:
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;
在PostgreSQL中(经过对SQL的适当翻译),效果如我所料:只有一个事务可以成功提交。这与我对 PostgreSQL 所描述的 SERIALIZABLE 的理解以及引用 ANSI 标准的其他来源一致:存在会产生相同效果的事务的串行执行。 returns 0 搜索结果然后添加条目的这两个事务没有串行执行。
在 MySQL 5.7 中,两个事务都成功并且 table 中有 2 个“Bob Ross”条目。 MySQL文档在禁止脏读、nonrepeatable读和幻读方面定义了SERIALIZABLE
;它没有提及串行执行的存在。
SQL由于其保守的锁定策略,至少在其默认模式下,它也正确地防止了双重插入。
我的问题:在这种情况下 MySQL 的行为是否正确,或者允许这些交易都成功是否违反了 SQL 标准? 我怀疑答案可能取决于“效果”的定义——从第一个 SELECT
观察到空结果集是否算作具有相同效果的两次连续执行的“效果”?
一些其他评论来帮助确定这个问题的范围:
- 我知道我可以在 MySQL 中实现所需的行为,方法是首先使用
ON CONFLICT IGNORE
执行插入,然后执行 select。我试图理解为什么等效标准 SQL 在两个引擎中没有表现出相同的行为。
- 我知道我也可以通过在
name
字段上设置唯一约束来修复它,无论如何这可以说是一个更好的数据模型。但核心问题仍然存在:为什么这些交易都成功了?
SQL 标准在 4.35.4 章 SQL-transactions 的隔离级别(强调我的):
The execution of concurrent SQL-transactions at isolation level SERIALIZABLE is guaranteed to be serializable. A serializable execution is defined to be an execution of the operations of concurrently executing SQL-transactions that produces the same effect as some serial execution of those same SQL-transactions. A serial execution is one in which each SQL-transaction executes to completion before the next SQL-transaction begins.
再往下一点,继续混淆问题:
The isolation level specifies the kind of phenomena that can occur during the execution of concurrent SQL-transactions. The following phenomena are possible:
[skipping definition of P1 (“Dirty read”), P2 (“Non-repeatable read”) and P3 (“Phantom”)]
The four isolation levels guarantee that each SQL-transaction will be executed completely or not at all, and that no updates will be lost. The isolation levels are different with respect to phenomena P1, P2, and P3. Table 8, “SQL-transaction isolation levels and the three phenomena” specifies the phenomena that are possible and not
possible for a given isolation level.
+------------------+--------------+--------------+--------------+
| Level | P1 | P2 | P3 |
+------------------+--------------+--------------+--------------+
| READ UNCOMMITTED | Possible | Possible | Possible |
+------------------+--------------+--------------+--------------+
| READ COMMITTED | Not Possible | Possible | Possible |
+------------------+--------------+--------------+--------------+
| REPEATABLE READ | Not Possible | Not Possible | Possible |
+------------------+--------------+--------------+--------------+
| SERIALIZABLE | Not Possible | Not Possible | Not Possible |
+------------------+--------------+--------------+--------------+
NOTE 53 — The exclusion of these phenomena for SQL-transactions executing at isolation level SERIALIZABLE is a consequence of the requirement that such transactions be serializable.
这种措辞带来了不幸的后果,许多实施者认为排除直接读取、non-repeatable 读取和幻读就足以正确实施 SERIALIZABLE
隔离级别,即使注释阐明这 不是定义,而是定义 的结果。
所以我认为 MySQL 是错误的,但它不是唯一的:Oracle 数据库以相同的方式解释 SERIALIZABLE
。
我无法在 MySQL 5.7 中重现此内容。其他事务总是出错:
ERROR 1213 (40001): Deadlock found when trying to get lock;
原因是 SELECT 没有在 WHERE 部分使用索引列,因此它为找到的每一行设置 s-locks,为找到的行和下一个行之间的每个间隙设置 gap-s-lock -key 在找到最后一行后锁定到正无穷大。所以在这种情况下并发插入是不可能的。
您得到结果的一个可能原因可能是:
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
它只为下一个事务设置隔离级别。如果您在此之后执行了一个 SELECT,隔离级别会变回正常(可重复读取)。
更好用
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;
当使用 SERIALIZABLE
事务来实现仅在数据库不存在时才将值插入数据库的模式时,我观察到 MySQL 和 Postgre[=52= 之间存在显着差异] 在他们对 SERIALIZABLE
隔离级别的定义中。
考虑以下 table:
CREATE TABLE person (
person_id INTEGER PRIMARY KEY AUTO_INCREMENT,
name VARCHAR NOT NULL
);
以及以下插入代码,运行 同时在两个连接上:
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;
在PostgreSQL中(经过对SQL的适当翻译),效果如我所料:只有一个事务可以成功提交。这与我对 PostgreSQL 所描述的 SERIALIZABLE 的理解以及引用 ANSI 标准的其他来源一致:存在会产生相同效果的事务的串行执行。 returns 0 搜索结果然后添加条目的这两个事务没有串行执行。
在 MySQL 5.7 中,两个事务都成功并且 table 中有 2 个“Bob Ross”条目。 MySQL文档在禁止脏读、nonrepeatable读和幻读方面定义了SERIALIZABLE
;它没有提及串行执行的存在。
SQL由于其保守的锁定策略,至少在其默认模式下,它也正确地防止了双重插入。
我的问题:在这种情况下 MySQL 的行为是否正确,或者允许这些交易都成功是否违反了 SQL 标准? 我怀疑答案可能取决于“效果”的定义——从第一个 SELECT
观察到空结果集是否算作具有相同效果的两次连续执行的“效果”?
一些其他评论来帮助确定这个问题的范围:
- 我知道我可以在 MySQL 中实现所需的行为,方法是首先使用
ON CONFLICT IGNORE
执行插入,然后执行 select。我试图理解为什么等效标准 SQL 在两个引擎中没有表现出相同的行为。 - 我知道我也可以通过在
name
字段上设置唯一约束来修复它,无论如何这可以说是一个更好的数据模型。但核心问题仍然存在:为什么这些交易都成功了?
SQL 标准在 4.35.4 章 SQL-transactions 的隔离级别(强调我的):
The execution of concurrent SQL-transactions at isolation level SERIALIZABLE is guaranteed to be serializable. A serializable execution is defined to be an execution of the operations of concurrently executing SQL-transactions that produces the same effect as some serial execution of those same SQL-transactions. A serial execution is one in which each SQL-transaction executes to completion before the next SQL-transaction begins.
再往下一点,继续混淆问题:
The isolation level specifies the kind of phenomena that can occur during the execution of concurrent SQL-transactions. The following phenomena are possible:
[skipping definition of P1 (“Dirty read”), P2 (“Non-repeatable read”) and P3 (“Phantom”)]
The four isolation levels guarantee that each SQL-transaction will be executed completely or not at all, and that no updates will be lost. The isolation levels are different with respect to phenomena P1, P2, and P3. Table 8, “SQL-transaction isolation levels and the three phenomena” specifies the phenomena that are possible and not possible for a given isolation level.
+------------------+--------------+--------------+--------------+ | Level | P1 | P2 | P3 | +------------------+--------------+--------------+--------------+ | READ UNCOMMITTED | Possible | Possible | Possible | +------------------+--------------+--------------+--------------+ | READ COMMITTED | Not Possible | Possible | Possible | +------------------+--------------+--------------+--------------+ | REPEATABLE READ | Not Possible | Not Possible | Possible | +------------------+--------------+--------------+--------------+ | SERIALIZABLE | Not Possible | Not Possible | Not Possible | +------------------+--------------+--------------+--------------+
NOTE 53 — The exclusion of these phenomena for SQL-transactions executing at isolation level SERIALIZABLE is a consequence of the requirement that such transactions be serializable.
这种措辞带来了不幸的后果,许多实施者认为排除直接读取、non-repeatable 读取和幻读就足以正确实施 SERIALIZABLE
隔离级别,即使注释阐明这 不是定义,而是定义 的结果。
所以我认为 MySQL 是错误的,但它不是唯一的:Oracle 数据库以相同的方式解释 SERIALIZABLE
。
我无法在 MySQL 5.7 中重现此内容。其他事务总是出错:
ERROR 1213 (40001): Deadlock found when trying to get lock;
原因是 SELECT 没有在 WHERE 部分使用索引列,因此它为找到的每一行设置 s-locks,为找到的行和下一个行之间的每个间隙设置 gap-s-lock -key 在找到最后一行后锁定到正无穷大。所以在这种情况下并发插入是不可能的。
您得到结果的一个可能原因可能是:
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
它只为下一个事务设置隔离级别。如果您在此之后执行了一个 SELECT,隔离级别会变回正常(可重复读取)。
更好用
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;