在软件开发中,事务(Transaction) 是保证数据一致性和完整性的核心机制,广泛应用于数据库操作、分布式系统等场景。以下是关于事务的详细解释:
一、事务的基本概念
定义:
事务是一组完整的不可分割的操作单元,要么全部成功执行,要么全部失败回滚。
典型场景:
- 银行转账:A 账户扣款与 B 账户收款必须同时成功或失败。
- 订单处理:创建订单、扣减库存、记录日志需作为一个整体执行。
二、ACID 特性
事务的核心特性(ACID)是其设计的基石:
-
原子性(Atomicity)
- 定义:事务中的操作要么全部成功,要么全部失败。
- 实现方式:通过数据库的回滚日志(Undo Log) 实现,失败时撤销已执行的操作。
-
一致性(Consistency)
- 定义:事务执行前后,数据从一个合法状态转换到另一个合法状态。
- 示例:转账前后,总金额不变。
- 注意:一致性依赖于应用层和数据库层共同保证。
这里举一个在实际开发使用增删改查的例子来说明一致性
- 新增(Insert) :比如给电商系统加一条订单记录,规则是 “订单金额必须大于 0”。如果事务里想插入一条金额为 0 的订单,一致性就会 “跳出来” 阻止 —— 要么插入失败(事务回滚),要么强迫你改成符合规则的数值,绝对不会让 “0 元订单” 这种不合理的数据存在
- 删除(Delete) :假设学校数据库里有个规则 “学生表和成绩表必须一一对应,不能有‘没学生的成绩’”。如果删除了一个学生,一致性会确保同时删掉他的所有成绩记录;要是删学生时成绩没删干净,事务就会回滚,保证不会留下 “孤儿成绩”
- 修改(Update) :像银行转账,A 给 B 转 100 元,规则是 “两人总余额不变”。如果 A 的余额扣了 100,但 B 的没加(比如系统卡了),一致性就会让整个操作失效 ——A 的钱退回去,B 的也不变,绝不会出现 “总金额少了 100” 的怪事
- 查询(Select) :虽然查询本身不修改数据,但一致性会保证你查到的是 “合理状态” 的数据~ 比如查库存时,不会让你看到 “正在被别人买,但还没扣完” 的中间状态,要么是没买之前的数量,要么是买完后的数量,清清楚楚
-
隔离性(Isolation)
-
定义:多个事务并发执行时,事物之间相互隔离,相互之间互不干扰,如同串行执行。
-
隔离级别(从低到高):
- 读未提交(Read Uncommitted) :允许读取未提交的数据(可能导致脏读)。
- 读已提交(Read Committed) :只允许读取已提交的数据(解决脏读)。
- 可重复读(Repeatable Read) :同一事务中多次读取结果一致(解决不可重复读)。
- 串行化(Serializable) :完全串行执行,解决所有并发问题。
-
-
持久性(Durability)
- 定义:事务提交后,其结果永久保存在数据库中,即使系统崩溃也不会丢失。
- 实现方式:通过数据库的重做日志(Redo Log) 和持久化存储实现。
三、事务的实现机制
1. 编程框架中的事务
-
Spring 框架(Java) :
java
命令:begin开启事务 commit提交事务 rollback回滚事务 在spring框架中,通过@Transaction注解来控制事务,该注解可以加在类上、方法上、接口上,但建议只加在方法上,不加在其他两个的上面。
@Transaction注解上有两个属性,分别是rollbackFor和propagation:
rollbackFor:出现指定的异常,进行事务回滚,如@Transaction(rollbackFor = Exception.class),出现该类异常就回滚
propagation:事务传播行为,假如事务A方法调用了事务B方法,B方法如何进行事务控制
propation中有几个参数,这里主要介绍常用的三个,分别是REQUIRED、REQUIRES_NEW、
SUPPORTS
REQUIRED:有事务,内部方法加入到外部方法的事务(增删改),就是B方法加入A方法的事务 当A方法发生回滚B也跟着回滚,不论B方法又没有执行成功。
REQUIRES_NEW:有事务,内部方法使用自己的事务(增删改),就是B方法不加入A方法的事务 当A方法发生回滚B方法不跟着回滚,B随着自己方法的成功而执行,失败而回滚。
SUPPORTS:如果外部有方法有事务,内部方法加入,如果外部方法没事务,内部方法也无事务(查询)
总结
@Transactional注解是 Spring 事务管理的强大工具,通过合理配置参数和应用位置,可以灵活控制事务的传播行为、隔离级别和回滚规则。使用时需注意方法可见性、自调用问题和异常处理,确保事务按预期工作。