Java找工作必备知识——day05事务详解(小王,小刘情景在线解释事务的属性)
Posted 结构化思维wz
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java找工作必备知识——day05事务详解(小王,小刘情景在线解释事务的属性)相关的知识,希望对你有一定的参考价值。
事务详解
文章目录
一、事务
基本术语:
- 事务(transaction)指一组 SQL 语句;
- 回退(rollback)指撤销指定 SQL 语句的过程;
- 提交(commit)指将未存储的 SQL 语句结果写入数据库表;
- 保留点(savepoint)指事务处理中设置的临时占位符(placeholder),你可以对它发布回退(与回退整个事务处理不同)。
概念:
事务指的是满足ACID特性的一组操作,可以通过Commit提交一个事务,也可以使用RollBack进行回滚。
ACID
1️⃣ 原子性(Atomicity)
事务呗视为不可分割的最小单元,事务的所有操作,姚明明全部提交成功,要么全部是被回滚。
2️⃣ 一致性(Consistency)
数据库在事务执行前后都保持一致性状态。在一致性状态下,所有事务对同一个数据的读取结果都是相同的。
3️⃣ 隔离性(Isolation)
一个事务所做的修改在最终提交以前,对其他事务是不可见的。
4️⃣ 持久性(Durability)
一旦事务提交,则起所做的修改将会永远保存到数据库中。即使系统发生崩溃,事务执行的结果也不能丢失。
关系:
AutoCommit
mysql 默认采用自动提交模式。也就是说,如果不显式使用START TRANSACTION
语句来开始一个事务,那么每个查询操作都会被当做一个事务并自动提交。
以上概念来自于github:cs_notes
二、事务属性详解
事务属性,就是描述事务特征的一系列值,就像描述人有身高体重等特征。
1.详解隔离属性
描述了事务解决并发问题的特征。
什么是并发?
多个事务(用户)在同一时间,访问操作了相同的数据。
- 同一时间:宏观的同一时间,有微小的前后。0.000几秒
并发会产生哪些问题?
- 脏读
- 不可重复读
- 幻影读
并发如何解决?
通过隔离属性解决,可以设置不同的值解决并发处理过程中的问题。
事务并发所产生的问题:
1️⃣ 脏读
一个事务读取了另一个事务中没有提交的数据。会在本事务中产生数据不一致的问题。
例子:小王跟小刘(小王媳妇),同时访问小王的银行卡。 共1000元。
👧 小刘要从小王的工资卡里取300块钱。这时候数据库肯定做更新操作(update(700))
👦 小王以0.0001秒的劣势也访问了工资卡,小王也要取钱,但是发现居然只有700块钱。于是就取了200块钱。这时候数据库(update500)
👧 小刘良心发现,觉得小王的私房钱已经够少了,决定不取了。这时候数据库rollback–>1000;
…晚上回家。
👨👧 : 小刘问小王,还有多少私房钱啊?看看是不是要再给小王一些零花钱。小王说:今天花了200,还有500了。 小刘大怒:什么?今天我看明明还有1000呢?你取200,另外三百呢???
👦 555555~ 跳进黄河也洗不清了。
通过本例,我们发现问题是,小王访问了小刘没有提交的数据。
💡 解决方案:**一个事务所做的修改在最终提交以前,对其他事务是不可见的。**读只能读已提交的数据!在spring中可以写@Transaction(Isonlation=Isonlation.READ_COMMITTED)
2️⃣ 不可重复读
一个事务中,多次读取相同的数据,但是读取结果不一样。会在本事务中产生数据不一致的问题。
例子:进过上次的教训,这次开启了事务。小王跟小刘并发访问银行卡数据。
👧 小刘以0.0001秒的优势先查了银行卡的余额。
👦 小王很快啊,取走200块钱,并且提交了事务。
👧 在事务还没提交的时候又做了其他操作,然后再看数据居然由1000变成了800。
这对小刘来说,出现了数据不一致的问题。而且800,不是脏数据,是已经提交的数据。
而且对小刘来说,是一次事务中发生的数据不一致!
💡 解决方案:隔离属性,加一把行锁。
小刘访问数据的时候被锁定了,小王就必须等小刘访问的事务结束。
在spring中可以写@Transaction(Isonlation=Isonlation.REPEATABLE_READ)
3️⃣ 幻影读
一个事务中,多次对整表进行查询统计,但是结果不一样,会在本事务中产生数据不一致的问题。
例子:一个事务访问一张表,并且统计数据。另一个事务插入了数据并且提交,这时候前面的事务统计数据就会不一致。
💡 解决方案:对应数据库底层的表锁
在spring中可以写@Transaction(Isonlation=Isonlation.SERIALIZABLE)
总结:
# 并发安全:表锁>行锁>READE_COMMITED
# 运行效率:表锁<行锁<READE_COMMITED
spring中默认的隔离属性:
Isonlation.DEFAULT 会调用不同数据库设置的默认隔离属性。
MYSQL:默认有行锁
Oracle:默认READE_COMMITED
select @@transaction_isolation; --mysql中查看默认隔离属性
隔离属性在实战中的建议:
推荐使用spring指定的ISOLATION_DEFAULT
并发概率低,没必要加很多锁来降低效率;如果真的遇到并发问题,我们更倾向于使用乐观锁,而不是物理锁的隔离属性。
mybatis没有自带乐观锁,可以使用自定义拦截器。
2.详解传播属性
描述了事务嵌套问题的特征。大事务中包含许多小事务。
Service调用service的特殊情况,有可能出现事务嵌套。
事务嵌套过程中,一旦某个出错,就会导致丧失了原子性
传播属性的值及其对应用法
传播属性的值 | 外部不存在事务 | 外部存在事务 | 用法 | 备注 |
---|---|---|---|---|
REQUIRED | 开启新的事务 | 融合到外部事务中 | @Transactional(propagation = Propagation.REQUIRED) | 增删改方法 |
SUPPORTS | 不开启事务 | 融合到外部事务中 | @Transactional(propagation = Propagation.SUPPORTS) | 查询方法 |
REQUIRES_NEW | 开启新的事务 | 挂起外部事务,创建新的事务 | @Transactional(propagation = Propagation.REQUIRES_NEW) | 日志记录方法中 |
NOT_SUPPORTED | 不开启事务 | 挂起外部事务 | @Transactional(propagation = Propagation.NOT_SUPPORTED) | 及其不常用 |
NEVER | 不开启事务 | 抛出异常 | @Transactional(propagation = Propagation.NEVER) | 及其不常用 |
MANDATORY | 抛出异常 | 融合到外部事务中 | @Transactional(propagation = Propagation.MANDATORY) |
默认的传播属性
REQUIRED是传播属性的默认值
推荐传播属性的使用方式
增删改 方法:直接使用默认值REQUIRED
查询 操作:显示指定传播属性的值为SUPPORTS
3.只读属性
针对于只进行查询操作的业务方法,可以加入只读属性,提供运行效率
默认值:false
4.超时属性
指定了事务等待的最长时间
1. 为什么事务进行等待?
当前事务访问数据时,有可能访问的数据被别的事务进行加锁的处理,那么此时本事务就必须进行等待。
2. 等待时间 秒
3. 如何应用 @Transactional(timeout=2)
4. 超时属性的默认值 -1
最终由对应的数据库来指定
5. 异常属性
Spring事务处理过程中
默认 对于RuntimeException及其子类 采用的是回滚的策略
默认 对于Exception及其子类 采用的是提交的策略
rollbackFor = {java.lang.Exception,xxx,xxx}
noRollbackFor = {java.lang.RuntimeException,xxx,xx}
@Transactional(rollbackFor = {java.lang.Exception.class},noRollbackFor = {java.lang.RuntimeException.class})
建议:实战中使用RuntimeExceptin及其子类 使用事务异常属性的默认值
三、事务属性常见配置总结
1. 隔离属性 默认值
2. 传播属性 Required(默认值) 增删改 Supports 查询操作
3. 只读属性 readOnly false 增删改 true 查询操作
4. 超时属性 默认值 -1
5. 异常属性 默认值
增删改操作 @Transactional
查询操作 @Transactional(propagation=Propagation.SUPPORTS,readOnly=true)
以上是关于Java找工作必备知识——day05事务详解(小王,小刘情景在线解释事务的属性)的主要内容,如果未能解决你的问题,请参考以下文章
创建servlet程序知识点详解---servlet-day05