写点什么

事务隔离级别实战学习

用户头像
U+2647
关注
发布于: 2021 年 04 月 09 日

事务的隔离性实现是最复杂的,也是最难的,所以 MySQL 对隔离性做了四个级别的实现。事务的隔离性其实是指,两个事务之间的操作在未提交时相关不可见。这跟 Java 多线程里的可见性正好相反。MySQL 通过 MVCC、锁等手段

1.1 读未提交(Read uncommitted)

这种事务隔离级别下,读到的数据是其他事务没有提交的数据,所以不需要做特殊处理,可以直接读取当前数据即可。

1.2 读已提交(read committed)

MySQL 通过 多版本并发控制(MVCC) 实现了 一致性非锁定读 ** 的能力。当一个事务对某个记录进行操作时,会对该行记录进行加锁,在 RC 级别下,如果另外一个事务要读取当前数据的话,则不会等待锁释放,而是读取行记录的一个快照版本。所以才叫非锁定读。因为读的是快照数据,所以也叫快照读**。


下面我们开启两个事务看下 RC 级别下的快照读情况。


首先修改 事务隔离级别为 RC 级别,并且设置 binlog 的模式


SET session transaction isolation level read committed;
复制代码


然后开启事务 A


begin;update my_test set name ="李四" where id = 1;
复制代码


先不提交,然后我们再打开一个事务 B。


begin;select * from my_test; +----+--------+------+| id | name   | age  |+----+--------+------+|  1 | 张三   |   11 |+----+--------+------+1 row in set (0.00 sec)
复制代码


然后我们提交一下事务 A,发现事务 B 已经能够读取到最新的数据了。


begin;select * from my_test; +----+--------+------+| id | name   | age  |+----+--------+------+|  1 | 张三   |   11 |+----+--------+------+1 row in set (0.00 sec)
select * from my_test; +----+--------+------+| id | name | age |+----+--------+------+| 1 | 李四 | 11 |+----+--------+------+1 row in set (0.00 sec)
复制代码


也就是说,我们可以直接读取到其他事务锁定的数据,这个就是非锁定读。读取到的数据是其他事务提交后的数据,没有提交的数据读取不到。所以隔离级别也叫读已提交。事务 B 两次查询请求的结果不一致的现象也叫不可重复读,即同一个事务里两次读取的结果不一致。这个问题在 RR 级别下就会解决。


整个 SQL 执行过程:


1.3 可重复读(repeatable read)

将事务隔离级别调整到 RR 级别。


SET session transaction isolation level repeatable read;
复制代码


在 RR 隔离级别下可以解决不可重复读的问题。使用的方法也是多版本并发控制(MVCC)


首先开启一个 事务 A。执行 读取数据。


begin;select * from my_test;+----+--------+------+| id | name   | age  |+----+--------+------+|  1 | 李四   |   11 |+----+--------+------+
复制代码


然后开始事务 B,执行更新操作,并提交。


begin;update my_test set name = "李四2" where id = 1;commit;
复制代码


然后事务 A 再执行读取操作,发现读取的结果没有变化。


begin;select * from my_test;+----+--------+------+| id | name   | age  |+----+--------+------+|  1 | 李四   |   11 |+----+--------+------+
select * from my_test;+----+--------+------+| id | name | age |+----+--------+------+| 1 | 李四 | 11 |+----+--------+------+
复制代码


所以是解决了不可重复读的问题。同一个事务里,第一次读取和第二次读取的数据是一致的。


但是如果你使用下面的语句进行查询的话,就会发现会读到最新的数据


select * from my_test lock in share mode;+----+---------+------+| id | name    | age  |+----+---------+------+|  1 | 李四2   |   11 |+----+---------+------+2 rows in set (0.00 sec)
select * from my_test for update;+----+---------+------+| id | name | age |+----+---------+------+| 1 | 李四2 | 11 |+----+---------+------+2 rows in set (0.00 sec)
复制代码


这是因为 MySQL 有两种读取方式。一种被称为快照读,一种被称为当前读。在 MVCC 中 select * from my_test 就是快照读,读取的是快照数据,而select * from my_test lock in share mode;select * from my_test for update; 是当前读,会读取当前版本的数据。MySQL 通过 MVCC 实现了上面这种能力。

1.4 串行化(Serializable)

所有 SQL 全部进行加锁处理,读加读锁排他锁,写加写排他锁。这样就不会有并发的问题了。但是性能很差。

发布于: 2021 年 04 月 09 日阅读数: 19
用户头像

U+2647

关注

evolving code monkey 2018.11.05 加入

https://zdran.com/

评论

发布
暂无评论
事务隔离级别实战学习