本地事务简介

本地事务简介

1 事务基本性质

数据库事务的几个特性:原子性(Automicity)、一致性(Consistency)、隔离性或独立性(islation)和持久性(Durability),简称ACID。

  • 原子性:一系列的操作,其整体不可拆分,要么同时成功,要么同时失败。

  • 一致性:数据在事务的前后,业务整体一致。

    • 转账 。A:1000; B:1000 ;转200
      事务成功。 A:800, B:1200
  • 隔离性:事务之间互相隔离。

  • 持久性:一旦事务成功,数据一定会记录在数据库。

2 事务的隔离级别
隔离级别特点
Read Uncommitted允许读取未提交的数据
Read committed只能读取已提交的数据
Repeatable Read同一事务中多次读取同一数据结果一致
Serializable事务完全串行化,按顺序执行
2.1 Read Uncommitted
场景描述

假设有一个银行账户表 accounts,记录用户的账户余额。现在有两个并发事务:

  1. 事务 A:从账户中扣除 100 元。
  2. 事务 B:查询账户余额。
数据库初始状态

假设账户初始余额为 1000 元。

事务操作步骤
  1. 事务 A 开始
    • 查询账户余额:SELECT balance FROM accounts WHERE account_id = 1;(结果为 1000 元)
    • 扣除 100 元:UPDATE accounts SET balance = balance - 100 WHERE account_id = 1;(此时账户余额变为 900 元,但事务 A 还未提交)
  2. 事务 B 开始
    • 查询账户余额:SELECT balance FROM accounts WHERE account_id = 1;(结果为 900 元,因为事务 A 的未提交数据被读取到了)
  3. 事务 A 回滚
    • 由于某些原因,事务 A 回滚,撤销了之前的更新操作。此时账户余额恢复为 1000 元。
  4. 事务 B 结束
    • 事务 B 查询到的余额是 900 元,但实际上账户的真实余额是 1000 元。
问题分析
  • 脏读:事务 B 读取到了事务 A 未提交的数据(900 元),而事务 A 最终回滚,导致事务 B 查询到的数据是不正确的。这种现象称为脏读
Read Uncommitted 的特点
  • Read Uncommitted 隔离级别下,事务可以读取到其他事务尚未提交的数据。
  • 这种隔离级别允许并发性能最高,但数据一致性最差,容易出现脏读问题。
2.2 Read Committed
场景描述

假设有一个库存表 inventory,记录商品的数量。现在有两个并发事务:

  1. 事务 A:更新商品的数量。
  2. 事务 B:查询商品的数量。
数据库初始状态

假设商品的初始数量为 100。

事务操作步骤
  1. 事务 A 开始
    • 查询商品数量:SELECT quantity FROM inventory WHERE product_id = 1;(结果为 100)
    • 更新商品数量:UPDATE inventory SET quantity = quantity - 10 WHERE product_id = 1;(此时商品数量变为 90,但事务 A 还未提交)
  2. 事务 B 开始
    • 查询商品数量:SELECT quantity FROM inventory WHERE product_id = 1;(结果为 100,因为事务 A 的更新尚未提交)
  3. 事务 A 提交
    • 提交事务 A,更新操作生效,商品数量变为 90。
  4. 事务 B 再次查询
    • 再次查询商品数量:SELECT quantity FROM inventory WHERE product_id = 1;(结果为 90,因为事务 A 已提交)
问题分析
  • 避免脏读:事务 B 在事务 A 提交之前查询到的商品数量是 100,而不是事务 A 未提交的 90。这避免了脏读问题。
  • 不可重复读:事务 B 在事务 A 提交后再次查询,结果从 100 变为 90。这种现象称为不可重复读,因为同一个事务在不同时间读取到的数据不一致。
  • 幻读:如果事务 B 在事务 A 提交之前查询商品数量,然后事务 A 插入了一条新的商品记录,事务 B 再次查询时可能会发现多了一条记录。这种现象称为幻读
Read Committed 的特点
  • 避免脏读:事务只能读取到其他事务已经提交的数据。
  • 可能出现不可重复读和幻读:由于事务 B 在事务 A 提交前后读取到的数据不一致,可能会出现不可重复读和幻读问题。
  • 并发性能较好:相比更高隔离级别(如 Repeatable ReadSerializable),Read Committed 的并发性能更好,因为它允许更多的并发操作。
2.3 Repeatable Read
场景描述

假设有一个商品库存表 inventory,记录商品的数量。现在有两个并发事务:

  1. 事务 A:查询并更新商品的数量。
  2. 事务 B:查询商品的数量。
数据库初始状态

假设商品的初始数量为 100。

事务操作步骤
  1. 事务 A 开始
    • 查询商品数量:SELECT quantity FROM inventory WHERE product_id = 1;(结果为 100)
    • 更新商品数量:UPDATE inventory SET quantity = quantity - 10 WHERE product_id = 1;(此时商品数量变为 90,但事务 A 还未提交)
  2. 事务 B 开始
    • 查询商品数量:SELECT quantity FROM inventory WHERE product_id = 1;(结果为 100,因为事务 A 的更新尚未提交)
  3. 事务 A 提交
    • 提交事务 A,更新操作生效,商品数量变为 90。
  4. 事务 B 再次查询
    • 再次查询商品数量:SELECT quantity FROM inventory WHERE product_id = 1;(结果仍为 100,因为事务 B 在同一个事务中,读取到的结果是一致的)
问题分析
  • 避免脏读:事务 B 在事务 A 提交之前查询到的商品数量是 100,而不是事务 A 未提交的 90。这避免了脏读问题。
  • 避免不可重复读:事务 B 在同一个事务中多次查询商品数量,结果始终为 100,即使事务 A 已提交,事务 B 仍然读取到的是事务开始时的一致数据。这避免了不可重复读问题。
  • 可能出现幻读:虽然 Repeatable Read 避免了不可重复读,但在某些数据库实现中,如果事务 A 插入或删除了记录,事务 B 可能会看到不同的结果集。这种现象称为幻读
Repeatable Read 的特点
  • 避免脏读和不可重复读:事务在同一个事务中多次读取同一数据的结果是一致的。
  • 可能出现幻读:在某些数据库实现中,如果事务 A 插入或删除了记录,事务 B 可能会看到不同的结果集。
  • 并发性能较好:相比 SerializableRepeatable Read 的并发性能更好,因为它允许更多的并发操作。
2.4 Serializable
场景描述

假设有一个商品库存表 inventory,记录商品的数量。现在有两个并发事务:

  1. 事务 A:查询并更新商品的数量。
  2. 事务 B:查询商品的数量。
数据库初始状态

假设商品的初始数量为 100。

事务操作步骤
  1. 事务 A 开始
    • 查询商品数量:SELECT quantity FROM inventory WHERE product_id = 1;(结果为 100)
    • 更新商品数量:UPDATE inventory SET quantity = quantity - 10 WHERE product_id = 1;(此时商品数量变为 90,但事务 A 还未提交)
  2. 事务 B 开始
    • 查询商品数量:SELECT quantity FROM inventory WHERE product_id = 1;(事务 B 会被阻塞,直到事务 A 提交或回滚)
  3. 事务 A 提交
    • 提交事务 A,更新操作生效,商品数量变为 90。
  4. 事务 B 继续执行
    • 查询商品数量:SELECT quantity FROM inventory WHERE product_id = 1;(结果为 90,因为事务 A 已提交)
问题分析
  • 避免脏读:事务 B 在事务 A 提交之前无法读取数据,因此不会读取到事务 A 未提交的数据。
  • 避免不可重复读:事务 B 在同一个事务中多次查询商品数量,结果始终为 90,即使事务 A 已提交,事务 B 仍然读取到的是事务开始时的一致数据。
  • 避免幻读:事务 B 在事务 A 提交之前无法读取数据,因此不会看到事务 A 插入或删除的记录。
Serializable 的特点
  • 完全避免并发问题Serializable 确保所有事务按顺序执行,完全避免了脏读、不可重复读和幻读。
  • 并发性能最低:由于事务必须按顺序执行,不能并行,因此并发性能最低。
  • 适用场景:适用于对数据一致性要求极高的场景,如金融交易系统、账务系统等。
3 事务的传播行为

在 Spring 中,事务传播行为(Transaction Propagation Behavior)定义了在一个事务中调用另一个事务方法时,事务如何被传播和管理。Spring 提供了多种事务传播行为,每种行为都对应不同的事务管理策略。以下是常见的事务传播行为及其解释:

传播行为当前有事务时当前无事务时
PROPAGATION_REQUIRED加入当前事务创建新事务
PROPAGATION_SUPPORTS加入当前事务非事务执行
PROPAGATION_MANDATORY加入当前事务抛出异常
PROPAGATION_REQUIRES_NEW创建新事务,挂起当前事务创建新事务
PROPAGATION_NOT_SUPPORTED挂起当前事务,非事务执行非事务执行
PROPAGATION_NEVER抛出异常非事务执行
PROPAGATION_NESTED创建嵌套事务创建新事务

实例代码:

#在注解上指定事务的传播行为
@Transational(Propagation=Propagation.REQUIRES_NEWS)
#方法签名
public void transaction(){...}
4 代理对象

在同一个类中,编写两个方法,内部调用的时候,会导致事务设置失效。原因是没有用到代理对象。

// TODO ps:貌似新版spring已经优化了,可以内部调用了。

解决方法:通过代理对象调用方法

①引入依赖

<dependency>
	<groupId>org.springframe.boot</groupId>
	<artifactId>spring-boot-starter-aop</artifactId>
</dependency>

②在启动类上加上注解@EnableAspectJAutoProxy(exposeProxy=true)

@EnableAspectJAutoProxy(exposeProxy=true)
@SpringBootApplication
public class Application {...}

③在类中使用代理对象如下:

OrderServiceImpl orderService = (OrderServiceImpl) AopContext.currentProxy();
// 在OrderServiceImpl类内部,通过代理对象调用自身的method1方法和method2方法
orderService.method1();
orderService.method2();
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值