事务的问题,是经常的问题。
基本概念:
事务(Transaction),一般是指要做的或所做的事情。在计算机术语中是指访问并可能更新数据库中各种数据项的一个程序执行单元(unit)。事务通常由高级数据库操纵语言或编程语言(如SQL,C++或Java)书写的用户程序的执行所引起,并用形如begin transaction和end transaction语句(或函数调用)来界定。事务由事务开始(begin transaction)和事务结束(end transaction)之间执行的全体操作组成。
定义数据库事务的逻辑:数据库事务是构成单一逻辑工作单元的操作集合
BEGIN TRANSACTION //事务开始
SQL1
SQL2
COMMIT/ROLLBACK //事务提交或回滚
关于事务的定义进一步解释如下:
-
1.数据库事务可以包含一个或多个数据库操作,但这些操作构成一个逻辑上的整体。
-
2.构成逻辑整体的这些数据库操作,要么全部执行成功,要么全部不执行。
-
3.构成事务的所有操作,要么全都对数据库产生影响,要么全都不产生影响,即不管事务是否执行成功,数据库总能保持一致性状态。
-
4.以上即使在数据库出现故障以及并发事务存在的情况下依然成立。
事务的ACID特性
A atomicity
事务中的所有操作作为一个整体像原子一样不可分割,要么全部成功,要么全部失败。
C consistency
事务的执行结果必须使数据库从一个一致性状态到另一个一致性状态。一致性状态是指:
1.系统的状态满足数据的完整性约束(主码,参照完整性,check约束等)
2.系统的状态反应数据库本应描述的现实世界的真实状态,比如转账前后两个账户的金额总和应该保持不变
。
I Isolation
并发执行的事务不会相互影响,其对数据库的影响和它们串行执行时一样。
比如多个用户同时往一个账户转账,最后账户的结果应该和他们按先后次序转账的结果一样。
D Durability
事务一旦提交,其对数据库的更新就是持久的。任何食物或者系统故障都不会导致数据的丢失。
在事物的ACID四大特性中,最核心、最根本的特性就是Consistency. 而对数据的一致性的破坏主要来自于两个方面:
1. 事物的并发执行 2. 事务故障或者系统故障
数据库系统是根据并发控制技术和日志恢复技术来避免这种情况发生的。
**并发控制技术 **保证了事务的隔离性,使数据库的一致性状态不会因为并发执行的操作被破坏。
日志恢复技术 保证了事务的原子性,使一致性状态不会因事务或系统故障被破坏。同时使已提交的对数据库的修改不会因系统崩溃而丢失,保证了事务的持久性。[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7kMQES7d-1605860394203)(./事务的特性.png)]
事务的隔离级别:
事务的隔离级别:
- 读未提交 read uncommitted
- 读已提交 read committed
- 可重复读 repeatable read
- 串行化 serializable
可能导致的并发异常:
- 脏写
- 脏读
- 不可重复读
- 幻读
- 丢失更新
具体对应的表格:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-onuKDhAu-1605860394205)(./数据库的隔离级别与可能发生的问题.png)]
@Transactional 注解的属性信息 的细节:
- 数据库的事物的注解属性信息
属性 | 描述 |
---|---|
name | 当在配置文件中有多个 TransactionManager , 可以用该属性指定选择哪个事务管理器 |
propagation | 事务的传播行为,默认值为 REQUIRED |
isolation | 事务的隔离度,默认值采用 DEFAULT |
timeout | 事务的超时时间,默认值为-1。如果超过该时间限制但事务还没有完成,则自动回滚事务 |
read-only | 指定事务是否为只读事务,默认值为 false;为了忽略那些不需要事务的方法,比如读取数据,可以设置 read-only 为 true |
rollback-for | 用于指定能够触发事务回滚的异常类型,如果有多个异常类型需要指定,各类型之间可以通过逗号分隔 |
no-rollback- for | 抛出 no-rollback-for 指定的异常类型,不回滚事务 |
- propagation 传播行为
- REQUIRED:如果有事务, 那么加入事务, 没有的话新建一个(默认情况下)
- NOT_SUPPORTED:容器不为这个方法开启事务
- REQUIRES_NEW:不管是否存在事务,都创建一个新的事务,原来的挂起,新的执行完毕,继续执行老的事务
- MANDATORY:必须在一个已有的事务中执行,否则抛出异常
- NEVER:必须在一个没有的事务中执行,否则抛出异常(与MANDATORY相反)
- SUPPORTS:如果其他bean调用这个方法,在其他bean中声明事务,那就用事务.如果其他bean没有声明事务,那就不用事务.
- NESTED: 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。
-
事物超时设置
@Transactional(timeout=30) //默认是30秒 -
事务隔离级别 isolation
- READ_UNCOMMITTED:读取未提交数据(会出现脏读, 不可重复读) 基本不使用
- READ_COMMITTED:读取已提交数据(会出现不可重复读和幻读)
- REPEATABLE_READ:可重复读(会出现幻读)
- SERIALIZABLE:串行化
注意:
- @Transactional 只能被应用到public方法上;仅仅 @Transactional 注解的出现不足于开启事务行为,它仅仅 是一种元数据。
- REQUIRED:如果存在一个事务,则支持当前事务。如果没有事务则开启一个新的事务。
- REPEATABLE_READ:这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻像读。它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了避免下面的情况产生(不可重复读)。
- readOnly:不允许只读 rollbackFor:回滚策略为Exception出现异常之后。
- TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); 函数内捕获异常时需要来设置事务回滚状态。
Spring Transactional一直是RD的事务神器,但是如果用不好,反会伤了自己。 @Transactional经常遇到的几个场景:
- @Transactional 加于private方法, 无效;
- @Transactional 加于未加入接口的public方法, 再通过普通接口方法调用, 无效;
- @Transactional 加于接口方法, 无论下面调用的是private或public方法, 都有效;
- @Transactional 加于接口方法后, 被本类普通接口方法直接调用, 无效;
- @Transactional 加于接口方法后, 被本类普通接口方法通过接口调用, 有效;
- @Transactional 加于接口方法后, 被它类的接口方法调用, 有效;
- @Transactional 加于接口方法后, 被它类的私有方法调用后, 有效。
对于事务隔离性的实现:
https://www.cnblogs.com/takumicx/p/9998844.html
对于事务注解的原理:
https://blog.csdn.net/baidu_39322753/article/details/100073169