MySQL:Transaction:修订间差异
第123行: | 第123行: | ||
* 在REPEATABLE READ级别下,InnoDB通过<syntaxhighlight lang="lua" inline>next-key locks</syntaxhighlight>防止幻读。 | * 在REPEATABLE READ级别下,InnoDB通过<syntaxhighlight lang="lua" inline>next-key locks</syntaxhighlight>防止幻读。 | ||
See: | |||
* https://dev.mysql.com/doc/refman/8.0/en/innodb-introduction.html | |||
[[Category:Database]] | [[Category:Database]] | ||
[[Category:MySQL]] | [[Category:MySQL]] |
2023年10月10日 (二) 12:13的版本
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: