1 什么是事务?
是数据库管理系统(DBMS)执行过程中的一个
逻辑单位
(不可再进行分割),由一个有限的数据库操作序列构成(多个DML
语句,select
语句不包含事务),要不全部成功
,要不全部不成功
。
2 事务特性
2.1 原子性(atomicity)
一个事务必须被视为一个不可分割
的最小单元,整个事务中的所有操作要么全部提交成功,要么全部失败,对于一个事务来说,不能只执行其中的一部分操作。
2.2 一致性(consistency)
事务将数据库从一种一致性状态
转换到另外一种一致性状态
,在事务开始之前
和事务结束之后
数据库中数据的完整性
没有被破坏。
2.3 隔离性(isolation)
一个事务的执行不能被其他事务干扰
。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的
,并发执行的各个事务之间不能互相干扰。
2.4 持久性(durability)
一旦事务提交,则其所做的修改就会永久保存
到数据库中。此时即使系统崩溃,已经提交的修改数据也不会丢失。
3 事务并发引发的问题
3.1 脏读
当一个事务读取到了另外一个事务修改但未提交
的数据。
T1 | T2 |
---|---|
begin | |
select money from t1 where id=King | begin |
update t1 set money=1500 where id=King | |
select money from t1 where id=King | |
rollback |
上表中,事务 T2 修改了一行记录,但是没有提交。然后事务 T1 读取到了未提交
的数据, 如果事务 T2 回滚
其更改的数据或者再次更新
,那么在事务 T1 中看到的记录可能就是错误的
。事务 T1 读取到了 King 老师余额为 1500
的记录,但是事务 T2 执行了回滚
操作,这时并不存在 King 老师余额为 1500 记录。
3.2 不可重复读
当事务内相同的记录被检索两次,且两次得到的结果不同时(强调修改
)。
T1 | T2 |
---|---|
begin | |
select money from t1 where id=King | begin |
update t1 set money=1500 where id=King | |
commit; | |
select money from t1 where id=King | |
commit; |
3.3 幻读(phantom read)
在事务执行过程中,另一个事务将新记录
添加到正在读取的事务中时,会发生幻读(强调新增
)。
T1 | T2 |
---|---|
begin | |
select money from t1 where id=King | begin |
insert into t1(id, money) values(King, 500); | |
commit; | |
select money from t1 where id=King | |
commit; |
那如果事务 T2 中是删除
了符合的记录而不是插入新记录,那事务T1中之后再根据条件读取的记录变少了,这种现象算不算幻读呢?
明确说一下,在
MySQL
中这种现象不属于幻读
,幻读强调的是一个事务按照某个相同条件多次读取记录时,后读取时读到了之前没有读到的记录。
那对于先前已经读到的记录,之后又读取不到这种情况,算啥呢?
其实这相当于对
每一条记录
都发生了不可重复读
的现象。幻读只是重点强调了读取
到了之前读取没有获取到的记录
。
4 隔离级别
READ UNCOMMITTED
:读未提交。READ COMMITTED
:读已提交。REPEATABLE READ
:可重复读。SERIALIZABLE
:可串行化。
4.1 SQL 标准中的四种隔离级别
SQL 标准
中规定,针对不同的隔离级别,并发事务可以发生不同严重程度的问题,具体情况如下:
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
READ UNCOMMITTED | 可能 | 可能 | 可能 |
READ COMMITTED | - | 可能 | 可能 |
REPEATABLE READ | - | - | 可能 |
SERIALIZABLE | - | - |
4.2 MySQL中的隔离级别
不同的数据库厂商对 SQL 标准中规定的四种隔离级别支持不一样,比方说 Oracle
就只支持 READ COMMITTED
和 SERIALIZABLE
隔离级别。本书中所讨论的 MySQL 虽然支持 4 种隔离级别,但与 SQL 标准中所规定的各级隔离级别允许发生的问题却有些出入,MySQL在REPEATABLE READ
隔离级别下,是可以禁止幻读问题的发生的
。
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
READ UNCOMMITTED | 可能 | 可能 | 可能 |
READ COMMITTED | - | 可能 | 可能 |
REPEATABLE READ | - | - | - |
SERIALIZABLE | - | - |
Tips: MySQL的
默认隔离级别
为REPEATABLE READ
。
4.2.1 如何设置事务的隔离级别?
我们可以通过下边的语句修改事务的隔离级别:
SET [GLOBAL|SESSION] TRANSACTION ISOLATION LEVEL level;
其中的 level 可选值有 4 个:REPEATABLE READ
| READ COMMITTED
| READ UNCOMMITTED
| SERIALIZABLE
。
使用
GLOBAL
关键字(在全局范围影响):
比方说这样:SET GLOBAL TRANSACTION ISOLATION LEVEL SERIALIZABLE;
则只对执行完该语句
之后
产生的会话起作用。当前已经存在的会话无效。使用
SESSION
关键字(在会话范围影响):
比方说这样:SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;
则对当前会话的
所有后续的事务有效
该语句可以在已经开启的事务中间执行
,但不会影响
当前正在执行的事务。
如果在事务之间执行
,则对后续的事务有效
。
- 上述两个关键字都不用(只对执行语句后的
下一个事务
产生影响):
比方说这样:
则只对当前会话中SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
下一个即将开启的事务有效
。下一个事务执行完后,后续事务将恢复
到之前的隔离级别。该语句不能
在已经开启的事务中间执行,会报错的
。