笔者环境(非必须):
MYSQL:5.7.18
操作系统:Mac 11.5.2
设置准备:
#设置事务不要自动提交;默认为1(自动提交)
set @@autocommit = 0;
select @@autocommit;
#设置隔离级别(注意!只在当前查询窗口有效)
set session transaction isolation level READ UNCOMMITTED; #以读未提交为例
show variables like '%tx_isolation%';
=》读未提交 READ UNCOMMITTED
=》读提交 READ COMMITTED
=》可重复读 REPEATABLE READ
=》串行化 SERIALIZABLE
一、读未提交(READ UNCOMMITTED)
#设置隔离级别:读未提交
set session transaction isolation level READ UNCOMMITTED;
设users.age=1;
执行顺序:
1.1)【事务1】Start Transaction;
1.2)【事务1】SELECT age FROM users WHERE id = 1; #查询结果为1
1.3)【事务1】UPDATE users SET age = 2 WHERE id = 1;
1.4)【事务1】SELECT age FROM users WHERE id = 1; #查询结果为2
1.5)【事务2】Start Transaction;(这里需注意,如果事务2是在事务1修改数据之前开启的,下面1.6的步骤读的结果还是修改之前的“1”)
1.6)【事务2】SELECT age FROM users WHERE id = 1;#查询结果为2(读得到其他事务未commit的数据)
================================ 分割线 =================================
二、读已提交(READ COMMITTED )
#设置隔离级别:读已提交
set session transaction isolation level READ COMMITTED;
设users.age=1;
执行顺序:
2.1)【事务1】Start Transaction;
2.2)【事务1】SELECT age FROM users WHERE id = 1; #查询结果为1
2.3)【事务1】UPDATE users SET age = 2 WHERE id = 1;
2.4)【事务1】SELECT age FROM users WHERE id = 1; #查询结果为2
2.5)【事务2】Start Transaction;
2.6)【事务2】SELECT age FROM users WHERE id = 1;#查询结果为1(读不到其他事务未commit的数据)
2.7)【事务1】COMMIT;
2.8)【事务2】SELECT age FROM users WHERE id = 1;#查询结果为2(读得到其他事务已commit的数据)
================================ 分割线 =================================
三、串行化(SERIALIZABLE)
串行化,对数据的读写都进行了一个加锁,也就是上一个事务未提交前,后面请求的事务是无法读取/修改数据的;这可很好地保持数据的一致性,但需要放弃并发的使用场景,因此也很少使用这种模式;
我们来实验一下:
设users.age=1;
3.1)【事务1】Start Transaction;
3.2)【事务1】SELECT age FROM users WHERE id = 1; #查询结果为1
3.3)【事务2】SELECT age FROM users WHERE id = 1; #查询结果为1
3.4)【事务1】UPDATE users SET age = 2 WHERE id = 1; #由于事务2没有提交commit,没有办法对数据进行修改,数据被锁,操作等待;
3.5)【事务2】用commit终止事务
3.6)【事务1】数据解锁,(3.4)步骤操作成功;
================================ 分割线 =================================
四、可重复读(REPEATABLE READ)
可重复读是数据的默认事务模式,也是我们用得最多的一种模式;
从字面意思可以理解为:
4.1)当数据开启、数据被加载进事务之后,能够在事务中重复被读取,也就是外面的事务对数据进行任何的操作,都不会影响数据在本次事务中的变化;以保证数据在具体一个业务事务中,数据不会突然地被改变,从而保证业务数据的合理性;
4.2)以每个事务提交数据(数据持久化)的顺序,对数据进行一个覆盖;
由于这个模式大家用得很多,就不在这列出实验步骤了;就是开启事务时候,数据只会在当前事务中受影响;不能理解的,可以仿造上面几种模式,自己试下!
五、几种模式下的使用场景:
面试官挺喜欢问这个问题的,笔者其实也没使用过;
本来想给大家搜集一下使用场景的,找了一圈资料后,发现除了我们常用的可重复读(REPEATABLE READ),其他模式的使用场景是真的很少;
我们试着理解一下:
5.1)读未提交&读已提交:
=》其实这两个模式比较接近,都是能够在事务的过程中,读到其他事务对数据更改的变化;
=》读未提交(俗称脏读):数据还没提交、也就是数据目前只是存在于mysql的内存中,数据还未被持久化;这种场景下,应该是要对数据有最高时效的读取;也就是数据一旦有任何业务上的变化,都要以“最新”的情况来读取;这个场景下对数据的实时要求比较高,举个不恰当的例子比如:天气、温度、股票、指数等等;(但随着缓存技术的发展,一般这种数据都不会交给数据库来处理了,个人认为!)
=》读已提交:它与脏读的区别点,就是它不认内存里的数据,它至少还是去读已经持久化在硬盘的数据;举个例子:假设,读取实时温度数据时,我希望对温度数据修改之前,同步也要修改湿度的数据才算有效;那么在“修改温度-修改湿度”这个事务提交之前,我还是去读旧的温度/湿度,以免两者不一致,而导致读出来的温度&湿度数据,并非同一时刻的;
5.2)串行化:
=》串行化第一个特征是要放弃掉“并行”的场景;
=》这里有一点要注意的是,大家其实都有阅读的权限。只是当有>=2个事务对资源进行占用时,数据库会给被重复占用资源进行一个加锁;那么这个时候,参与资源暂用的任何一个事务想对数据进行修改都是不行的;除非其他事务相继离去,只剩你自己一个事务还没结束时,你可以对数据进行修改;也正是这个原因,做不到数据并发地修改;
=》这种模式下,简直有点像对其他“不严谨”的模式的一种报复性心理的设计(哈哈哈!);可以想象这种模式下,对程序员控制锁的能力要求略高,因为你必须确保,当前业务只有一个访问者对其读写,否则数据库就很容易进入死锁;
=》好处是,数据一致性是很高的,不会出现数据紊乱的情况;
=》但使用前提是,你的场景不需要并发&你可以很好地控制锁、避免死锁的发生!
以上是对MYSQL数据库事务四种模式的实验,以及个人理解,
如有笔误、不对的、或理解偏差的地方,欢迎指出、一起交流学习!
纸上得来终觉浅,
希望对你有帮助!