微服务下保证事务的一致性

1、事务的介绍

1.1 事务

1.1.1 事务的产生

数据库中的数据是共享资源,因此数据库系统通常要支持多个用户的或不同应用程序的访问,并且各个访问进程都是独立执行的,这样就有可能出现并发存取数据的现象,这里有点类似Java开发中的多线程安全问题(解决共享变量安全存取问题),如果不采取一定措施会出现数据异常的情况。列举一个简单的经典案例:比如用户用银行卡的钱还京东白条,银行卡扣款成功了,但是白条因为网络或者系统问题没有还款成功,就会出大问题,这时候我们就需要使用事务。

1.1.2 事务的概念

事务是数据库操作的最小工作单元,是作为单个逻辑工作单元执行的一系列操作;这些操作作为一个整体一起向系统提交,要么都执行、要么都不执行;事务是一组不可再分割的操作集合(工作逻辑单元)。例如:在关系数据库中,一个事务可以是一条SQL语句,一组SQL语句或整个程序。

1.1.3 事务的特性

事务的四大特征主要是:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability),这四大特征大家或多或少都听说过,这里我做下简单介绍。

(1)原子性(Atomicity):事务内的操作要么全部成功,要么全部失败,不会在中间的某个环节结束。假如所有的操作都成功了,那么事务是成功的,只要其中任何一个操作失败,那么事务会进行回滚,回滚到操作最初的状态。

(2)一致性(Consistency):事务的执行使数据从一个状态转换为另一个状态,但是对于整个数据的完整性保持稳定。换一种说法是数据按照预期生效,数据的状态是预期的状态。比如数据库在一个事务执行之前和执行之后,都必须处于一致性状态,如果事务执行失败,那么需要自动回滚到原始状态,也就是事务一旦提交,其他事务查看到的结果一致,事务一旦回滚,其他事务也只能看到回滚前的状态。

举个通俗一点的例子:小明给小红转账100元,转账前和转账后数据是正确的状态,这叫一致性,如果小红没有收到100元或者收到金额少于100元,这就出现数据错误,就没有达到一致性。

(3)隔离性(Isolation):在并发环境中,不同事务同事修改相同的数据时,一个未完成的事务不会影响另外一个未完成的事务。

例如当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。

(4)持久性(Durability):事务一旦提交,其修改的数据将永远保存到数据库中,改变是永久性,即使接下来数据库发生故障也不应对其有任何影响。

1.1.4 Mysql隔离级别

如果不考虑事务隔离性产生问题:脏读、不可重复读和幻读。

Mysql隔离级别分为4种:Read Uncommitted(读取未提交的)、Read Committed(读取提交的)、Repeatable Red(可重复读)、Serializaable(串行化)

(1)Read Uncommitted是隔离级别最低的一种事务级别。在这种隔离级别下,一个事务会读到另一个事务更新后但未提交的数据,如果另一个事务回滚,那么当前事务读到的数据就是脏数据,这就是脏读(Dirty Read)。

(2)在Read Committed隔离级别下,一个事务可能会遇到不可重复读(Non Repeatable Read)的问题。不可重复读是指,在一个事务内,多次读同一数据,在这个事务还没有结束时,如果另一个事务恰好修改了这个数据,那么,在第一个事务中,两次读取的数据就可能不一致。

(3)在Repeatable Read隔离级别下,一个事务可能会遇到幻读(Phantom Read)的问题。幻读是指,在一个事务中,第一次查询某条记录,发现没有,但是,当试图更新这条不存在的记录时,竟然能成功,并且,再次读取同一条记录,它就神奇地出现了,就好象发生了幻觉一样。

(4)Serializable是最严格的隔离级别。在Serializable隔离级别下,所有事务按照次序依次执行,因此,脏读、不可重复读、幻读都不会出现。虽然Serializable隔离级别下的事务具有最高的安全性,但是,由于事务是串行执行,所以效率会大大下降,应用程序的性能会急剧降低。如果没有特别重要的情景,一般都不会使用Serializable隔离级别。

如果没有指定隔离级别,数据库就会使用默认的隔离级别。在MySQL中,如果使用InnoDB,默认的隔离级别是Repeatable Read。

1.1.5 启动事务

在说明启动事务之前,首先大家先想一下事务的传播行为,事务传播行为用于解决两个被事务管理的方法互相调用问题。实际开发中将事务在service控制,如以下方法调用存在传播行为,如果serviceB也会产生一个代理对象,同时也会进行事务管理,执行serviceA和serviceB分别开启事务,上边的serviceA中funA方法内容不处于一个事务中了。

class serviceA{
    //此方法进行事务控制
    funA(){
        //在此方法中操作多个dao的操作,处于一个事务中
        userDao.insertUser();
        orderDao.insertOrder();
        //如果在这里调用另一个service的方法,此时存在事务传播
        serviceB.funB();
    }
}
class serviceB{
    funB(){
    }
}

解决方案就是,在启动类上添加注解 @EnableTransactionManagement,在执行事务的方法上面使用 @Transactional(isolation = Isolation.DEFAULT,propagation = Propagation.REQUIRED)设置隔离界别与事务传播。默认就是REQUIRED。

Spring的声明式

### 分布式系统的微服务事务一致性解决方案 #### 理论基础与模型 为了理解如何确保分布式系统中的微服务之间保持事务一致性,首先要了解一些基本的理论模型。X/Open 分布式事务模型提供了一个框架,在此框架内定义了两阶段提交协议(2PC)[^1]。该协议用于协调多个资源管理器间的事务提交过程,以保证全局原子性。 #### 常见解决方案概述 针对分布式环境下的事务一致性的挑战,存在多种不同的策略: - **TCC 补偿型方案**:这种模式下,每个业务逻辑单元被设计成支持三个动作——Try(尝试)、Confirm(确认)和Cancel(取消)。它允许应用程序开发者显式控制这些阶段的行为,从而更好地适应复杂的业务需求[^5]。 - **基于可靠性消息的最终一致性方案**:利用消息队列作为中介者来传递命令或事件给其他服务实例。这种方法不仅实现了松耦合的服务间通信,而且借助于消息重试机制能够达到较高的可用性和容错能力[^4]。 - **最大努力通知型方案**:适用于那些对实时性要求不高但又希望尽可能多地完成工作的场景。这类方法通常会周期性地向目标端发送提醒直到得到回应为止。 #### 中间件工具Seata的应用 Seata 是一款开源项目,旨在简化跨多数据库和服务边界的ACID特性实现。其提供了两种主要的工作方式: - **AT 模式 (Automatic Transaction)**:自动识别SQL语句并将其转化为全局事务的一部分;无需修改现有代码即可享受分布式事务的好处。 - **Saga 模式**:一种长活事务编排引擎,特别适合处理涉及大量子任务且可能跨越长时间跨度的任务流。通过将整个流程拆分为一系列短小精悍的状态转换步骤,使得复杂业务更容易管理和维护。 #### 实践建议 当考虑实施上述任一方案时,应遵循以下最佳实践原则: - 尽量降低对原有业务逻辑的影响程度; - 关注整体性能表现,避免不必要的开销; - 设计合理的错误恢复路径,提高系统的健壮性; - 考虑引入合适的监控手段以便及时发现问题所在。 ```python from seata import ATMode, SagaMode def execute_transaction_with_seata(): with ATMode() as at_mode: # 执行需要加入分布式事务保护的操作 pass saga_flow = [ {'name': 'step_1', 'compensate': rollback_step_1}, {'name': 'step_2', 'compensate': rollback_step_2} ] with SagaMode(saga_flow) as saga_mode: # 启动由多个步骤组成的长工作流 pass ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值