概述
关于事务,有一个经典的例子:
张三转账给李四10块钱,需要两个步骤,第一,张三扣10块钱,第二,李四加10块钱。那么问题来了,如果第二个步骤失败了,那么张三的钱咋办?答案是回滚。
事务到底是什么?
就是在执行N个SQL语句时,要么都成功,不允许出现其中一部分成功,一部分失败的情况。一旦有任意一个SQL失败,那么将已经成功的SQL进行回滚,回到执行之前的状态
在mysql中,常用的存储引擎有InnoDB和MyISAM,其中MyISAM不支持事务,以下讲的事务都是基于InnoDB存储引擎
什么是事务隔离级别呢?
上面的例子只是单个事务执行,如果有多个事务时会发生什么呢?
假设张三扣了10块钱时,还没执行下一步,这时王五给张三打20了块钱,好巧不巧,李四在加10块钱时失败了,张三的账户金额需要回滚,这时候应该回滚到多少钱呢?
回滚了之后,王五如果后续操作也失败了,也要回滚张三的账户,那么就会有两个事物都要回滚张三的余额,这时候数据就错乱了
简单来说,事物隔离级别存在就是为了解决:多个事务在操作同一行数据时,应该怎么处理?
事务隔离级别
标准的SQL设计中,事务隔离级别主要有以下几种:
隔离级别 | 中文 |
---|---|
serializable | 串行化 |
repeatable read | 可重复读 |
read committed | 读提交 |
read uncommitted | 读未提交 |
serializable 串行化
该隔离级别是最严格的隔离级别,当一个启动事务时,将会对要读写的记录加锁,在事务结束之前,其他任务事务都无法对加锁的记录进行任何操作,包括写操作
这意味着数据可以保证绝对无误,不会有任何的污染,但锁带来的就是性能的损失。串行化是性能最低的事务隔离级别
Repeatable Read 可重复读
这是MySQL中InnoDB默认的隔离级别 ,在该隔离级别下,一个事务中,读到数据始终是一样的(前提是自己没有修改它),举个例子:
有一条记录money = 1
,事务A启动,读到该记录,然后事务B修改 money=2
,并且提交事务,这时事务A再一次读取该记录,发现money仍然等于1。
示意图如下:
用一句话来形容该事务隔离级别就是:别的事务怎么样,跟我没关系
Read Committed 读提交
读提交的意思是,可以读到提交的事务。也可以叫做“不可重复读”,这是Oracle数据库的默认事务隔离级别。
再以上面的例子,如果是在Read Committed 隔离级别下,那么事务A第二次读的时候就可以读到money=2
,因为事务B已经提交了,提交了就能被读到了
示意图如下:
Read Uncommitted 读未提交
该事务隔离级别下,即便是未提交的数据,也能被其他事务给读取到,因此该隔离级别是不安全的,因为未提交的数据,将来可能是要回滚的,也就是说被其他事务读走的数据,可能是错误数据
修改事务隔离级别
可以使用以下命令查看当前事务隔离级别:
mysql> show variables like '%tx_isolation%';
+---------------+-----------------+
| Variable_name | Value |
+---------------+-----------------+
| tx_isolation | REPEATABLE-READ |
+---------------+-----------------+
1 row in set (0.01 sec)
PS: 如果是MySQL5.7及以上版本,使用transaction_isolation
替代 tx_isolation
临时修改
set [ global | session ] transaction isolation level Read uncommitted | Read committed | Repeatable | Serializable;
该命令中,global
和session
是可选的,含义如下:
- global:修改全局session隔离级别
- session:修改当前session隔离级别
- 不填:仅修改下一个session的隔离级别
例如,将事务隔离级别改为Read committed
set global transaction isolation level Read committed
注意:修改后需要退出再登录才能看到修改的事务隔离级别
永久修改
永久修改需要修改mysql的配置文件,在my.cnf的[mysqld]下加上:
transaction-isolation=Read-Committed
重启服务
脏读与幻读
脏读就是读到了错误的脏数据,在Read Uncommited读未提交的事务隔离级别下可能发生脏读。
例如事务A读到并将money由1修改为2,并且未提交,而此时事务B读到了money=2,接着事务A回滚了,money又回到了1,事务B就错误的读到了脏数据money=2
示意图如下:
幻读是在多次读的时候得到了不一样的结果,宛如幻觉,举个例子,事务A查询id=1的记录,发现不存在,准备插入id=1这条记录,此时事务B也插入了id=1,导致事务A因主键冲突而操作失败,事务A再次发现,发现id=1的记录不存在啊,那为啥我会主键冲突呢?幻觉?
示意图如下:
各个事务隔离级别下脏读幻读现象:
事务隔离级别 | 脏读 | 幻读 |
---|---|---|
serializable 串行化 | ❌ | ❌ |
Repeatable Read 可重复读 | ❌ | ✔ |
Read Committed 读提交 | ❌ | ✔ |
Read Uncommitted 读未提交 | ✔ | ✔ |