一、并发场景下事务的数据问题
1.1 脏读
事务A 读取了 事务B 为提交的数据,事务B 回滚,那么 事务A 读的就是脏数据
1.2 不可重复读
事务A 读取 同一数据,事务B 在这个过程中进行修改,导致 事务A 每次读的数据都不一致。
1.3 幻读
有一个学校,所有学生都打了疫苗,事务A 需要将所有学生都标注为已打疫苗状态。 事务B 这时候插入了一条未打疫苗的新生,事务A 发现数据没改完,和发生幻觉一样,这就是幻读。
二、MySQL的四种隔离级别
2.1 隔离级别介绍
隔离级别 | 效果 |
---|---|
读未提交(read-uncommitted) | 一个事务没提交时,它操作的结果能被另一条事务锁能看到。 |
不可重复读(read-committed) | 一个事务没提交时,它操作的结果不能被另一条事务所能看到。 |
可重复读(repeatable-read) | 一个事务查询一条数据,即使这条数据被别的事务所更改,但是这个事务查询的开始和结束的这个时间段内都是一致的。 |
串行化(serializable) | 正如物理书上写的,串行是单线路,顾名思义在MySQL中同一时刻只允许单个事务执行,“写”会加“写锁”,“读”会加“读锁”。当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行。(其它事务是可以开启事务的,并不是说其它事务不能在开启事务了,只是在同时开启完事务后不能同时更改。读加读锁,写加写锁。) |
2.2 隔离级别的问题解决情况
脏读 | 不可重复读 | 幻读 | |
---|---|---|---|
读未提交(RU) | × | × | × |
读提交(RC) | √ | × | × |
可重复读(RR) | √ | √ | √ |
串行(xíng)化(S) | √ | √ | √ |
三、隔离级别的修改和查看
3.1 查看当前会话的隔离级别
mysql> select @@tx_isolation;
+-----------------+
| @@tx_isolation |
+-----------------+
| REPEATABLE-READ |
+-----------------+
1 row in set, 1 warning (0.00 sec)
3.2 查看当前系统会话的隔离级别
mysql> select @@global.tx_isolation;
+-----------------------+
| @@global.tx_isolation |
+-----------------------+
| REPEATABLE-READ |
+-----------------------+
1 row in set, 1 warning (0.00 sec)
3.3 设置会话的隔离级别,隔离级别由低到高设置依次为
set session transaction isolation level read uncommitted;
set session transaction isolation level read committed;
set session transaction isolation level repeatable read;
set session transaction isolation level serializable;
3.4 设置当前系统的隔离级别,隔离级别由低到高设置依次为
set global transaction isolation level read uncommitted;
set global transaction isolation level read committed;
set global transaction isolation level repeatable read;
set global transaction isolation level serializable;
四、案例分析
4.1 读未提交(RU)
左窗口,修改陈梦
的年龄为26,并未提交事务:
右窗口,查询陈梦
的年龄,查出了左窗口修改后并未提交的数据:
造成了脏读的问题。
4.2 读提交(RC)
左窗口,修改马龙
30,并未提交事务:
右窗口,查询马龙
的年龄,发现依然为32:
左窗口提交事务,右窗口再次查看,发现马龙
的年龄成为了30:
此隔离级别,可解决脏读问题,但造成了不可重复读问题。
4.3 可重复读(RR)
首先,左窗口开启事务,进行查询所有数据,右窗口同样开始起事务并查询所有数据,左窗口更新马龙
的年龄为29并未提交:
右窗口进行查询,发现马龙的年龄依然为30:
左窗口提交,右窗口继续查询,发现马龙的年龄依然为30:
右窗口提交事务后,再次查询,发现马龙的年龄变为了29:
这样就实现了在这一事务内可以重复读取一条数据。
4.4 串行化(SERIALIZABLE)
窗口1:开启事务,并且查询id为1的数据:
窗口2:开启事务,查询id为1的数据后并进行修改,但是事务1中查询已经加锁了(我怀疑是行锁的共享锁,因为事务2也能查这条数据,说明事务2也能持有这个共享锁,事务1也不能进行修改,造成死锁。但是这两个事务又能修改id不为1的数据,由此可见,串行化中的查询此id,加的是行的共享锁)