Spring源码解析-事务


一 理论

写前:
spring源码中关于事务的解析是一项工程十分复杂化的解析过程,耐心的阅读之后,会学习很多,从18年开始阅读spring源码到现在,中间断断续续的也是静下心来读了好几遍(都是每一个专栏读的受不了就停了一下,很是惭愧),加上本人有一点点的热爱源码阅读,所以也是承接自己的笔记,慢慢的记录下来,本篇博客中,一些基础的理论知识和相关源码的解析是有参考了相关的,源码的阅读是参考了对应了《Spring源码深度解析》(引用了网络博客:Spring源码解析-事务

1,事务的特性

事务的四个特性(ACID):
原子性(Atomicity):事务是一个原子操作,由一系列的动作组成,事务的原子性确保动作要么全部执行,要么完全不起作用。
一致性(Consistency):一旦事务完成(不管成功还是失败),系统必须保证它所建模的业务处于一致的状态,而不是部分完成部分失败。
隔离性(Isolation):可能有很多的事务会同时处理相同的数据,因此每个事务都应该和其他事务隔离开来,防止数据损坏。
持久性(Durability):一旦事务完成,无论发生什么系统错误,它的结果都不应该受到影响,通常情况下,事务的结果被写到持久化容器中。

2,事务的隔离级别

隔离规则:定义了一个事务可能受到其他并发事务影响的程度
并发事务引起的问题
脏读:脏读发生在一个事务读取了另一个事务改写但尚未提交的数据时,如果改写在稍后被回滚了,那么第一个事务获取的数据就是无效的。
不可重复读:不可重复读发生在一个事务执行相同的查询两次或两次以上,但是每次都得到不同的数据时。这通常是因为另一个并发事务在两次查询期间进行了更新。
幻读:幻读与不可重复读类似。它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录。

脏读、不可重复读都是因为并发事务在修改同一份数据的时候导致的问题,这些问题可以通过对同一个记录加锁的方式来解决。幻读则是并发事务对不同数据操作时导致的问题,此种情况只能通过表锁、事务的串行化来解决。

隔离级别

隔离级别 含义
ISOLATION_READ_UNCOMMITTED(读未提交) 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读,在该隔离级别下,并发事务对同一数据的操作,读是没有加锁的,写是行级共享锁
ISOLATION_READ_UNCOMMITTED(读提交) 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生,在该隔离级别下,并发事务对同一数据的操作,读是行级共享锁,读完立即释放锁,写则是行级互斥锁,直到事务提交才释放,这样保证同一时间只能有一个事务写,其他事务无法读,当事务提交释放锁之后,才可以读,并发读不影响,
ISOLATION_REPEATABLE_READ(可重复读) 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生,该隔离级别下,并发事务对于同一数据的操作,读写都是行级互斥锁,事务提交后才会释放锁,所以实现了对于同一数据的并发事务的串行化。只有一个事务操作完数据,其他事务才能继续操作
ISOLATION_SERIALIZABLE(序列化) 最高的隔离级别,完全服从ACID的隔离级别,确保阻止脏读、不可重复读以及幻读,也是最慢的事务隔离级别,因为它通常是通过完全锁定事务相关的数据库表来实现的,该隔离级别下,并发事务对于同一表的操作,会通过表锁来实现并发事务的串行化,可以彻底解决一致性的所有问题。当然,带来的问题便是性能急剧下降,对于并发不大需要保证数据安全性的可以使用该隔离级别。
3,mysql的事务隔离级别及行锁,互斥锁,共享锁

mysql中默认的事务隔离级别:REPEATABLE-READ (可重复读)

共享锁:又称为读锁(S锁),一个事务获取了共享锁,其他事务可以获取共享锁,不能获取互斥锁,其他事务可以进行读操作,不能进行写操作
互斥锁:又称为写锁(X锁),如果事务T对数据A加上互斥锁后,则其他事务不能再对A加上任何类型的锁,获准互斥锁的事务既能进行读数据,也能进行写数据。
对于insert,update,delete,InnoDB会自动给涉及的数据加互斥锁
对于一般的Select语句,InnoDB不会加任何锁,事务可以通过以下语句给显示加共享锁或排他锁。
共享锁:SELECT … LOCK IN SHARE MODE;
排他锁:SELECT … FOR UPDATE;
示例:mysql默认的事务隔离级别为可重复读

并发1:

#首先设置不自动提交事务,需要手动提交,默认的情况下,执行一条语句就是一个事务
mysql> set autocommit = 0;
#显示的再select语句后面增加一个共享锁,不提交事务
mysql> select * from users where id =1 LOCK IN SHARE MODE;
+----+-----------+----------+-------+----------+---------------------+----------+-----------+
| id | userName  | passWord | email | nickName | regTime             | user_sex | nick_name |
+----+-----------+----------+-------+----------+---------------------+----------+-----------+
|  1 | zhoucg_wl | 12       | 12    | 12       | 2019-08-15 21:50:53 | 12       | 12        |
+----+-----------+----------+-------+----------+---------------------+----------+-----------+
1 row in set (0.01 sec)

#执行提交事务之后,这个时候会在并发2的界面中看到提交成功
mysql> commit
#再次执行select语句,增加一个互斥锁(排斥锁),不执行commit语句
#显式获取排它锁,其他进程无法再获取该记录的锁,但是可以查看该记录。这个时候可以再并发2中查看对应的该条记录,不能更新操作
mysql> select * from users where id =1 for update;
+----+---------------+----------+-------+----------+---------------------+----------+-----------+
| id | userName      | passWord | email | nickName | regTime             | user_sex | nick_name |
+----+---------------+----------+-------+----------+---------------------+----------+-----------+
|  1 | zhoucg_wl7961 | 12       | 12    | 12       | 2019-08-15 21:50:53 | 12       | 12        |
+----+---------------+----------+-------+----------+---------------------+----------+-----------+
1 row in set (0.01 sec)

mysql> 

并发2

#这个时候,执行update语句根据指定数据的时候,会进入等待(在并发1执行commit语句之前),阻塞
mysql> update users set userName = 'zhoucg_wl7961' where id =1;
#并发1执行commit之后,会执行成功
Query OK, 1 row affected (46.24 sec)
Rows matched: 1  Changed: 1  Warnings: 0
mysql> 

如何修改mysql中的事务的隔离级别:
1,查看mysql的事务隔离级别:

show variables like 'tx_isolation';

2,设置mysql的事务隔离级别:

#语法
set [global|session] transaction isolation level [read uncommitted | read committed | repeatable read | serialization];
#设置
set session transaction isolation level read committed;
#查看
select @@global.tx_isolation, @@session.tx_isolation;  

MySQL锁,sql执行细节

2,事务的传播行为

事务的传播行为是指当一个事务的方法调用另一个事务的方法时,必须指定事务如何进行传播

传播行为 含义 描述
PROPAGATION_REQUIRED 当前方法必须运行在事务中,如果当前事务存在,方法将会在该事务中运行,如果不存在,新建一个事务。 Spring默认的事务隔离级别
PROPAGATION_SUPPORTS 如果存在一个事务,支持当前事务,如果没有事务,则非事务的执行。
PROPAGATION_MANDATORY 如果已经存在一个事务,则支持当前事务,如果没有一个活动的事务,抛出异常
PROPAGATION_REQUIRES_NEW 总是开启一个新的事务,如果一个事务存在,则将这个存在的事务挂起,
PROPAGATION_NOT_SUPPORTED 总是非事务地执行,并挂起任何存在的事务
PROPAGATION_NEVER 总是非事务的执行,如果存在一个活动的事务,则抛出异常
PROPAGATION_NESTED 如果一个活动的事务存在,则运行在嵌套的事务中,如果没有活动的事务,按照TransactionDefinition.PROPAGATION_REQUIRED

Spring事务传播行为详解

二 源码

源码解读,II. 事务自定义标签

发布了55 篇原创文章 · 获赞 14 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/zcswl7961/article/details/102524326