这回,数据库事务4种隔离级别及7种传播行为,终于说明清楚了

事务的特性:

原子性:事务的不可分割,组成事务的各个逻辑单元不可分割。

一致性:事务执行的前后,数据完整性保持一致。

隔离性:事务执行不应该受到其他事务的干扰。

持久性:事务一旦结束,数据就持久化到数据库中。

查看/设置隔离级别

   查看:SELECT @@tx_isolation  设置:set tx_isolation='xxx'

事务的隔离级别

如果不考虑隔离性,引发一些安全问题

隔离性:一个事务的执行,不应该受到其他事务的干扰。

脏读:一个事务读到了另一个事务未提交的数据,导致查询结果不一致
不可重复读:一个事务读到了另一个事务已经提交的update的数据,导致多次查询结果不一致。
虚读/幻读:一个事务读到了另一个事务已经提交的insert的数据,导致多次查询结果不一致。

设置事务的隔离级别:

read uncommitted (读取未提交内容)   :脏读,不可重复读,虚读都有可能发生
read committed (读取提交内容)   :避免脏读。但是不可重复读和虚读是有可能发生
repeatable read (可重读)   :避免脏读和不可重复读,但是虚读有可能发生。
serializable(可串行化)        :避免脏读,不可重复读,虚读。

通过实例演示四种隔离级别

   read uncommitted (读取未提交内容)  :

   事务A:开启事务并执行 SELECT * FROM testtest, 但并不提交事务

set tx_isolation='read-uncommitted';

start transaction;

SELECT * FROM testtest;
复制代码

 结果显示:

事务B:开始事务并执行  UPDATE testtest set age = 200 WHERE name = 'zhangsan'; 但并不提交事务

start transaction;
UPDATE testtest set age = 200 WHERE name = 'zhangsan';
复制代码

事务A:再次执行  SELECT * FROM testtest

在事务B未提交的情况下,仍然读到了修改的200,出现了脏读!

read committed (读取提交内容)

 事务A:开启事务并执行 SELECT * FROM testtest, 但并不提交事务

   

事务B:开始事务并执行  UPDATE testtest set age = 200 WHERE name = 'zhangsan'; 但并不提交事务

start transaction;
UPDATE testtest set age = 200 WHERE name = 'zhangsan';
复制代码

事务A:再次执行  SELECT * FROM testtest

   

在事务B未提交的情况下,没有读到修改的200,避免了脏读!

事务B:提交事务。

事务A:再次执行  SELECT * FROM testtest

在事务B提交的情况下,读到了修改的200,出现了不可重复读!(同一个事务中多次读取结果不一致)

repeatable read (可重读)  mysql默认

事务A:开启事务并执行 SELECT * FROM testtest, 但并不提交事务

   

事务B:开始事务并执行  UPDATE testtest set age = 200 WHERE name = 'zhangsan'; 但并不提交事务

start transaction;
UPDATE testtest set age = 200 WHERE name = 'zhangsan';
复制代码

事务A:再次执行  SELECT * FROM testtest

   

在事务B未提交的情况下,没有读到修改的200,避免了脏读!   

事务B:提交事务。

事务A:再次执行  SELECT * FROM testtest

在事务B提交的情况下,没有读到修改的200,避免了不可重复读!

再次 开启事务B,并添加一条记录,并提交事务

start transaction;
INSERT INTO testtest(name,age) VALUES ('wangwu','100');
COMMIT;
复制代码

事务A:再次执行  SELECT * FROM testtest

并没有读到新增的记录

事务A:插入刚才事务B新增的记录

INSERT INTO testtest(name,age) VALUES ('wangwu','100');
复制代码

发现插不进去,因为事务B已经添加并提交事务了(我们name字段做了唯一索引),出现了幻读!(查询的时候没有,但插入的时候确实存在,跟出现幻觉一样)。

serializable(可串行化)

事务A:开启事务并执行 SELECT * FROM testtest, 但并不提交事务

start transaction;

SELECT * FROM testtest;
复制代码

事务B:开始事务并执行  UPDATE testtest set age = 200 WHERE name = 'zhangsan'

发现事务B停在那里了,没有任何执行,直到事务A提交事务。简言之,它是在每个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争。

补充

以上四种隔离级别最高的是Serializable级别,最低的是Read uncommitted级别,当然级别越高,执行效率就越低。像Serializable这样的级别,就是以锁表的方式(类似于Java多线程中的锁)使得其他的线程只能在锁外等待,所以平时选用何种隔离级别应该根据实际情况。在MySQL数据库中默认的隔离级别为Repeatable read (可重复读)。

在MySQL数据库中,支持上面四种隔离级别,默认的为Repeatable read (可重复读);而在Oracle数据库中,只支持Serializable (串行化)级别和Read committed (读已提交)这两种级别,其中默认的为Read committed级别

传播行为

1、PROPAGATION_REQUIRED:如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,该设置是最常用的设置。

2、PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作

3、PROPAGATION_SUPPORTS:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行。‘

4、PROPAGATION_MANDATORY:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就抛出异常。

5、PROPAGATION_REQUIRES_NEW:支持当前事务,创建新事务,无论当前存不存在事务,都创建新事务。

6、PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。

7、PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。

扩展:www.cnblogs.com/523823-wu/p…

作者:Anti-Mage

链接:blog.csdn.net/u013083284/… 来源:csdn

最后说一下最容易混淆多的一个点: spring的事务是什么?与数据库的事务是否一样

本质上其实是同一个概念,spring的事务是对数据库的事务的封装,最后本质的实现还是在数据库,假如数据库不支持事务的话,spring的事务是没有作用的.数据库的事务说简单就只有开启,回滚和关闭,spring对数据库事务的包装,原理就是拿一个数据连接,根据spring的事务配置,操作这个数据连接对数据库进行事务开启,回滚或关闭操作。

猜你喜欢

转载自blog.csdn.net/m0_64355285/article/details/121855545