跳转至

MySQL 事务_事务隔离级别详解

使用事务语法

  1. 开启事务 start transaction,可以简写为 begin
  2. 然后记录之后需要执行的一组 sql
  3. 提交 commit
  4. 如果所有的 sql 都执行成功,则提交,将 sql 的执行结果持久化到数据表内。
  5. 回滚 rollback
  6. 如果存在失败的 sql,则需要回滚,将 sql 的执行结果,退回到事务开始之时
  7. 无论回滚还是提交,都会关闭事务!需要再次开启,才能使用。
  8. 还有一点需要注意,就是事务只针对当前连接。

下面我们来进行演示: > 使用第一个链接 A,开启事务后,执行一条 update 语句。 结果成功,数据已经变成修改之后!

这里写图片描述

此时我们没有提交。 再从其他连接 B 来查看,发现数据末更改:

这里写图片描述

此时如果连接 A 选择提交,也就是 commit 操作。则连接 B 的数据也会发生变化。 > 而如果连接 A 选择回滚,也就是 rollback 操作。则连接 A 再次查询则发现数据还原。


基本原理

语法说完了,浮躁的人也不用继续看下去了。下面简单说一下事务的基本原理吧。提交,就会将结果持久化,不提交就不会。 > 如果我们不开启事务,只执行一条 sql,马上就会持久化数据,可以看出,普通的执行就是立即提交。这是因为 MySQL 默认对 sql 语句的执行是自动提交的。 > 也就是说,开启事务,实际上就是关闭了自动提交的功能,改成了 commit 手动提交! > 我们可以通过简单的对是否自动提交加以设置,完成开启事务的目的! 自动提交的特征是保存在服务的一个 autocommit 的变量内。可以进行修改:

set autocommit = 0;关闭 > > set autocommit = 1;开启

还需要注意一点,就是事务类似于外键约束,只被 innodb 引擎支持。


特点

下面来说说事务的特点 ACID。也就是原子性,一致性,隔离性和持久性。

原子性: 事务的一组操作是原子的不可再分割的,这组操作要么同时完成要么同时不完成。类似于一个 CAS(compare and swap)(有时间会讲解 cas)

一致性: 事务在执行前后数据的完整性保持不变。(例:原本的外键约束在进行事务成功后不会损坏)

隔离: 当多个事务同时操作一个数据库时,可能存在并发问题,此时应保证各个事务要进行隔离,事务之间不能互相干扰。

持久性: 事务一旦被提交,就不可能再被回滚!


事务并发

事务并发会带来一些问题,所以才有了不同的事务隔离级别。要想了解事务的隔离级别,就必须首先了解事务并发会带来的问题。 一般来说,会出现三类数据读问题和数据更新问题。

脏读

一个事务正在对一条记录做修改,但未提交,另一个事务读取了这些脏数据,并进一步处理,就会产生未提交的数据依赖。 举一个例子:

时间

转账事务 A

取款事务 B

T1

开始事务

T2

开始事务

T3

查询账户余额为 1000 元

T4

取出 500 元把余额改为 500 元

T5

查询账户余额为 500 元(脏读)

T6

撤销事务余额恢复为 1000 元

T7

汇入 100 元把余额改为 600 元

T8

提交事务

A 读取了 B 尚未提交的脏数,导致最后余额为 600 元。

不可重复读

一个事务在不同时间读取数据不一致。 举一个例子:

时间

取款事务 A

转账事务 B

T1

开始事务

T2

开始事务

T3

查询账户余额为 1000 元

T4

查询账户余额为 1000 元

T5

取出 100 元把余额改为 900 元

T6

提交事务

T7

查询账户余额为 900 元(和 T4 读取的不一致)

可以看到最后读取的数据不一致。

幻读

幻读和不可重复读的概念类似,都是不同时间数据不一致,只不过幻读是针对新增数据,而不可重复读是针对更改数据。 看一个例子:

时间

统计金额事务 A

转账事务 B

T1

开始事务

T2

开始事务

T3

查询用户 id 为 8 的数据,结果为空,执行插入(id 为 pk)

T4

新增一个 id 为 8 的数据

T5

提交事务

T6

提交事务,插入报错


隔离级别

事务并发带来的问题前文已经描述得非常仔细了。事务的隔离级别就是为了针对并发出现的问题,不同的级别可以保证不同的一致性。

为了解决上面讲到的并发事务处理带来的问题,SQL 标准提出了 4 个等级的事务隔离级别。不同的隔离级别在同样的环境下会出现不同的结果。

隔离级别设置及查看 > mysql> select @@tx_isolation; +-----------------+ | @@tx_isolation | +-----------------+ | REPEATABLE-READ | +-----------------+ 1 row in set (0.00 sec)

mysql> set tx_isolation = 0; (备注:0 - 3 对应数据库四种隔离级别) Query OK, 0 rows affected (0.00 sec) 下面看看四种隔离级别的比较: > Read Uncommitted(读取未提交内容)

在该隔离级别,所有事务都可以看到其他未提交事务的执行结果。本隔离级别很少用于实际应用,因为它的性能也不比其他级别好多少。读取未提交的数据,也被称之为脏读(Dirty Read)。 > Read Committed(读取提交内容)

这是大多数数据库系统的默认隔离级别(但不是 MySQL 默认的)。它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变。这种隔离级别也支持所谓的不可重复读(Nonrepeatable Read),因为同一事务的其他实例在该实例处理其间可能会有新的 commit,所以同一 select 可能返回不同结果。 > Repeatable Read(可重读)

这是 MySQL 的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。不过理论上,这会导致另一个棘手的问题:幻读(Phantom Read)。简单的说,幻读指当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影”行。InnoDB 和 Falcon 存储引擎通过多版本并发控制(MVCC,Multiversion Concurrency Control)机制解决了该问题。

Serializable(可串行化) 这是最高的隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争。

隔离级别

读数据一致性

脏读

不可重复读

幻读

未提交读(Read uncommitted)

最低级别,只能保证不读取物理上损坏的数据

已提交读(Read committed)

语句级

可重复读(Repeatable read)

事务级

可序列化(Serializable)

最高级别,事务级