MySQL:Transaction

来自WHY42
Riguz留言 | 贡献2023年10月10日 (二) 12:13的版本 →‎MySQL

ACID

ACID,指数据库事务正确执行的四个基本要素的缩写。包含:

  • 原子性(Atomicity)
  • 一致性(Consistency)
  • 隔离性(Isolation)
  • 持久性(Durability)

一个支持事务(Transaction)的数据库,必需要具有这四种特性,否则在事务过程(Transaction processing)当中无法保证数据的正确性,交易过程极可能达不到交易方的要求。

原子性

整个事务中的所有操作,要么全部完成,要么全部不完成,不可能停滞在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。

一致性

  • 强一致性:读操作可以立即读到提交的更新操作。
  • 弱一致性:提交的更新操作,不一定立即会被读操作读到,此种情况会存在一个不一致窗口,指的是读操作可以读到最新值的一段时间。
  • 最终一致性:是弱一致性的特例。事务更新一份数据,最终一致性保证在没有其他事务更新同样的值的话,最终所有的事务都会读到之前事务更新的最新值。如果没有错误发生,不一致窗口的大小依赖于:通信延迟,系统负载等。

其他一致性变体还有:

  • 单调一致性:如果一个进程已经读到一个值,那么后续不会读到更早的值。
  • 会话一致性:保证客户端和服务器交互的会话过程中,读操作可以读到更新操作后的最新值。

隔离性

脏读Dirty reads

A dirty read (aka uncommitted dependency) occurs when a transaction is allowed to read data from a row that has been modified by another running transaction and not yet committed.

事务A修改了一个数据,但未提交,事务B读到了事务A未提交的更新结果,如果事务A提交失败,事务B读到的就是脏数据。

Transaction 1 Transaction 2
/* Query 1 */
SELECT age FROM users WHERE id = 1;
/* will read 20 */
-
-
/* Query 2 */
UPDATE users SET age = 21 WHERE id = 1;
/* No commit here */
/* Query 1 */
SELECT age FROM users WHERE id = 1;
/* will read 21 */
-
-
ROLLBACK; /* lock-based DIRTY READ */

不可重复读 Non-repeatable reads

在同一个事务中,对于同一份数据读取到的结果不一致。比如,事务B在事务A提交前读到的结果,和提交后读到的结果可能不同。不可重复读出现的原因就是事务并发修改记录,要避免这种情况,最简单的方法就是对要修改的记录加锁,这回导致锁竞争加剧,影响性能。另一种方法是通过MVCC可以在无锁的情况下,避免不可重复读。

Transaction 1 Transaction 2
/* Query 1 */
SELECT * FROM users WHERE id = 1;
-
-
/* Query 2 */
UPDATE users SET age = 21 WHERE id = 1;
COMMIT; /* in multiversion concurrency
   control, or lock-based READ COMMITTED */
/* Query 1 */
SELECT * FROM users WHERE id = 1;
COMMIT; /* lock-based REPEATABLE READ */
-

幻读Phantom reads

在同一个事务中,同一个查询多次返回的结果不一致。事务A新增了一条记录,事务B在事务A提交前后各执行了一次查询操作,发现后一次比前一次多了一条记录。幻读是由于并发事务增加记录导致的,这个不能像不可重复读通过记录加锁解决,因为对于新增的记录根本无法加锁。需要将事务串行化,才能避免幻读。

Transaction 1 Transaction 2
/* Query 1 */
SELECT * FROM users
WHERE age BETWEEN 10 AND 30;
-
-
/* Query 2 */
INSERT INTO users(id, name, age) VALUES (3, 'Bob', 27);
COMMIT;
/* Query 1 */
SELECT * FROM users
WHERE age BETWEEN 10 AND 30;
COMMIT;
-

隔离级别(SQL:1992)

事务的隔离级别从低到高有:

Read Uncommitted
最低的隔离级别,什么都不需要做,一个事务可以读到另一个事务未提交的结果。所有的并发事务问题都会发生。
Read Committed
只有在事务提交后,其更新结果才会被其他事务看见。可以解决脏读问题。
Repeatable reads
在一个事务中,对于同一份数据的读取结果总是相同的,无论是否有其他事务对这份数据进行操作,以及这个事务是否提交。可以解决脏读、不可重复读。
Serializable
事务串行化执行,隔离级别最高,牺牲了系统的并发性。可以解决并发事务的所有问题。

持久性

事务提交后,对系统的影响是永久的。

MySQL

MySQL支持SQL92标准中定义的各种事务隔离级别。InnoDB引擎默认支持的级别是REPEATABLE READ

  • 在REPEATABLE READ级别下,InnoDB通过next-key locks防止幻读。

See: