分布式事务-LCN

目录

一、什么是事务

事务的四大特性:ACID

分布式事务产生的原因

二、CAP&BASE理论

CAP理论

BASE理论

三、核心步骤

四、代码

事务协调者

事务发起方代码如下

配置文件

Mapper接口

OpenFeign

接口api

启动类

事务参与方

配置文件

Mapper接口

接口Api

启动类


​​​​​​​

一、什么是事务

事务:是数据库操作的最小工作单元,是作为单个逻辑工作单元执行的一系列操作;这些操作作为一个整体一起向系统提交,要么都执行、要么都不执行;事务是一组不可再分割的操作集合(工作逻辑单元

事务的四大特性:ACID

  1. ​​​​​​​Atomicity(原子性):一个事务(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被恢复(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。

  2. Consistency(一致性):在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设规则,这包含资料的精确度、串联性以及后续数据库可以自发性地完成预定的工作。  

  3. Isolation(隔离性):数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括读未提交(Read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(Serializable)。
  4. Durability(持久性):事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。

分布式事务产生的原因

随着互联网的迅速发展,单体项目已经不能满足日益复杂的业务需求。后来逐渐演变出分布式。既然有分布式项目,就相应的会产生分布式事务。分布式系统会把一个应用系统拆分为可独立部署的多个服务,因此需要服务与服务之间远程协作才能完成事务操作,这种分布式系统环境下由不同的服务之间通过网络远程协作完成事务称之为分布式事务。比如订单系统,订单完成之后,需要调用会员系统给用户积分,此时,若积分业务成功,然后订单系统突然发生异常,此时就会涉及到分布式事务。

二、CAP&BASE理论

CAP理论

CAP定律

    这个定理的内容是指的是在一个分布式系统中、Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),三者不可得兼。

一致性(C)

    在分布式系统中的所有数据备份,在同一时刻是否同样的值。(等同于所有节点访问同一份最新的数据副本)

可用性(A)

    在集群中一部分节点故障后,集群整体是否还能响应客户端的读写请求。(对数据更新具备高可用性)

分区容错性(P)

    以实际效果而言,分区相当于对通信的时限要求。系统如果不能在时限内达成数据一致性,就意味着发生了分区的情况,必须就当前操作在C和A之间做出选择。

BASE理论

 BASE是Basically Available(基本可用)、Soft state(软状态)和 Eventually consistent(最终一致性)三个短语的缩写。BASE理论是对CAP中一致性和可用性权衡的结果,其来源于对大规模互联网系统分布式实践的总结, 是基于CAP定理逐步演化而来的。BASE理论的核心思想是:即使无法做到强一致性,但每个应用都可以根据自身业务特点,采用适当的方式来使系统达到最终一致性。

基本可用

    基本可用是指分布式系统在出现不可预知故障的时候,允许损失部分可用性—-注意,这绝不等价于系统不可用。比如:

(1)响应时间上的损失。正常情况下,一个在线搜索引擎需要在0.5秒之内返回给用户相应的查询结果,但由于出现故障,查询结果的响应时间增加了1~2秒

(2)系统功能上的损失:正常情况下,在一个电子商务网站上进行购物的时候,消费者几乎能够顺利完成每一笔订单,但是在一些节日大促购物高峰的时候,由于消费者的购物行为激增,为了保护购物系统的稳定性,部分消费者可能会被引导到一个降级页面

软状态

    软状态指允许系统中的数据存在中间状态,并认为该中间状态的存在不会影响系统的整体可用性,即允许系统在不同节点的数据副本之间进行数据同步的过程存在延时

最终一致性

    最终一致性强调的是所有的数据副本,在经过一段时间的同步之后,最终都能够达到一个一致的状态。因此,最终一致性的本质是需要系统保证最终数据能够达到一致,而不需要实时保证系统数据的强一致性。

三、核心步骤

  • 创建事务组
    是指在事务发起方开始执行业务代码之前先调用TxManager创建事务组对象,然后拿到事务标示GroupId的过程。

  • 加入事务组
    添加事务组是指参与方在执行完业务方法以后,将该模块的事务信息通知给TxManager的操作。

  • 通知事务组

  • 是指在发起方执行完业务代码以后,将发起方执行结果状态通知给TxManager,TxManager将根据事务最终状态和事务组的信息来通知相应的参与模块提交或回滚事务,并返回结果给事务发起方。

  • 摘自  https://www.codingapi.com/docs/txlcn-preface/

四、代码

事务协调者

下载源码 https://github.com/codingapi/tx-lcn/releases/tag/5.0.2.RELEASE

源码里有sql语句,创建数据库tx-managr,然后创建表

修改txlcn-tm配置文件

spring.application.name=TransactionManager
server.port=7970
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/tx-manager?characterEncoding=UTF-8
spring.datasource.username=root
spring.datasource.password=root
spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.hibernate.ddl-auto=update
#redis
spring.redis.host=192.168.6.130
spring.redis.port=6379
spring.redis.password=xiaojie
spring.redis.timeout=5000
#配置日志
tx-lcn.logger.enabled=true
tx.log
#后台登录的密码
tx-lcn.manager.admin-key=123456

登录后台地址:http://127.0.0.1:7970/admin/index.html#/ 密码为123456

事务发起方代码如下

配置文件

spring:
  application:
    name: xiaojie-order
  cloud:
    nacos:
      server-addr: 127.0.0.1:8848
      discovery:
        enabled: true
  datasource:
    url: jdbc:mysql://localhost:3306/order?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver
server:
  port: 8090
tx-lcn:
  client:
    manager-address: 127.0.0.1:8070
  logger:
    enabled: true

Mapper接口

package com.xiaojie.lcn.api.order.impl.mapper;

import org.apache.ibatis.annotations.Update;

/**
 * @Description:
 * @author: xiaojie
 * @date: 2021.07.19
 */
public interface OrderMapper {

    @Update("update tb_order set orderState=1 where orderId=#{orderId}")
    Integer finishOrder(String orderId);
}

OpenFeign

package com.xiaojie.lcn.api.order.impl.service.feign;

import com.xiaojie.api.member.service.ScoreService;
import org.springframework.cloud.openfeign.FeignClient;

/**
 * @Description:
 * @author: xiaojie
 * @date: 2021.07.19
 */
@FeignClient("xiaojie-member")
public interface ScoreServiceFeign extends ScoreService {
}

接口api

package com.xiaojie.lcn.api.order.impl.service.impl;

import com.codingapi.txlcn.tc.annotation.LcnTransaction;
import com.xiaojie.api.member.entity.Score;
import com.xiaojie.lcn.api.order.impl.mapper.OrderMapper;
import com.xiaojie.lcn.api.order.impl.service.feign.ScoreServiceFeign;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Description:
 * @author: xiaojie
 * @date: 2021.07.19
 */
@RestController
public class OrderServiceImpl {

    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private ScoreServiceFeign scoreServiceFeign;

    @GetMapping("/finish")
    @Transactional 
    @LcnTransaction  //Lcn事务注解
    public String finishOrder(String orderId){
        Integer integer = orderMapper.finishOrder(orderId);
        if (integer<0){
            return "修改订单失败";
        }
        //订单完成,添加积分
        Score score=new Score();
        score.setOrderId(orderId);
        score.setUserId(123456L);
        Integer integer1 = scoreServiceFeign.saveScore(score);
        int i=1/Integer.valueOf(orderId);
        return integer1+"";
    }
}

启动类

package com.xiaojie.lcn.api.order.impl;

import com.codingapi.txlcn.tc.config.EnableDistributedTransaction;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;

/**
 * @Description:
 * @author: xiaojie
 * @date: 2021.07.19
 */
@SpringBootApplication
@EnableDistributedTransaction //开启分布式事务
@EnableFeignClients
@MapperScan("com.xiaojie.lcn.api.order.impl.mapper")
public class OrderApp {
    public static void main(String[] args) {
        SpringApplication.run(OrderApp.class);
    }
}

事务参与方

配置文件

spring:
  application:
    name: xiaojie-member
  cloud:
    nacos:
      server-addr: 127.0.0.1:8848
      discovery:
        enabled: true
  datasource:
    url: jdbc:mysql://localhost:3306/score?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver
server:
  port: 8080
tx-lcn:
  client:
    manager-address: 127.0.0.1:8070
  logger:
    enabled: true

Mapper接口

package com.xiaojie.lcn.api.member.impl.mapper;

import com.xiaojie.api.member.entity.Score;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Options;

/**
 * @Description:
 * @author: xiaojie
 * @date: 2021.07.19
 */
public interface ScoreMapper {
    @Insert("INSERT INTO tb_score (userId,orderId) VALUES(#{userId},#{orderId})")
    @Options(useGeneratedKeys = true, keyProperty = "id")
    Integer save(Score score);
}

接口Api

package com.xiaojie.lcn.api.member.impl.service;

import com.codingapi.txlcn.tc.annotation.LcnTransaction;
import com.xiaojie.api.member.entity.Score;
import com.xiaojie.api.member.service.ScoreService;
import com.xiaojie.lcn.api.member.impl.mapper.ScoreMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Description: 积分服务
 * @author: xiaojie
 * @date: 2021.07.19
 */
@RestController
public class ScoreServiceImpl  implements ScoreService {
    @Autowired
    private ScoreMapper scoreMapper;
    @Override
    @Transactional
    @LcnTransaction
    public Integer saveScore(Score score) {
        return scoreMapper.save(score);
    }
}

启动类

package com.xiaojie.lcn.api.member.impl;

import com.codingapi.txlcn.tc.config.EnableDistributedTransaction;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;

/**
 * @Description:
 * @author: xiaojie
 * @date: 2021.07.19
 */
@SpringBootApplication
@EnableDistributedTransaction
@EnableFeignClients
@MapperScan("com.xiaojie.lcn.api.member.impl.mapper")
public class MemberApp {
    public static void main(String[] args) {
        SpringApplication.run(MemberApp.class);
    }
}

完整实例请参考https://github.com/xiaojiewhisper/xiaojie-lcn.git

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
编辑推荐: 本文来自于csdn,本篇文章主要介绍了LCN5.0.2有3种模式,分别是LCN模式,TCC模式,TXC模式,希望对您的学习 有所帮助。 一、简介 LCN分布式事务框架其本身并不创建事务,而是基于对本地事务的协调从而达到事务一致性的效果。 LCN模式: LCN模式是通过代理Connection的方式实现对本地事务的操作,然后在由TxManager统一协调控制事务。当本地事务提交回滚或者关闭连接时将会执行假操作,该代理的连接将由LCN连接池管理。 该模式的特点: - 该模式对代码的嵌入性为低。 - 该模式仅限于本地存在连接对象且可通过连接对象控制事务的模块。 - 该模式下的事务提交与回滚是由本地事务方控制,对于数据一致性上有较高的保障。 - 该模式缺陷在于代理的连接需要随事务发起方一共释放连接,增加了连接占用的时间。 TCC模式: TCC事务机制相对于传统事务机制(X/Open XA Two-Phase-Commit),其特征在于它不依赖资源管理器(RM)对XA的支持,而是通过对(由业务系统提供的)业务逻辑的调度来实现分布式事务。主要由三步操作,Try: 尝试执行业务、 Confirm:确认执行业务、 Cancel: 取消执行业务。 该模式的特点: - 该模式对代码的嵌入性高,要求每个业务需要写三种步骤的操作。 - 该模式对有无本地事务控制都可以支持使用面广。 - 数据一致性控制几乎完全由开发者控制,对业务开发难度要求高。 TXC模式: TXC模式命名来源于淘宝,实现原理是在执行SQL之前,先查询SQL的影响数据,然后保存执行的SQL快走信息和创建锁。当需要回滚的时候就采用这些记录数据回滚数据库,目前锁实现依赖redis分布式锁控制。 该模式的特点: - 该模式同样对代码的嵌入性低。 - 该模式仅限于对支持SQL方式的模块支持。 - 该模式由于每次执行SQL之前需要先查询影响数据,因此相比LCN模式消耗资源与时间要多。 - 该模式不会占用数据库的连接资源。 二、原理 核心步骤 1.创建事务组 是指在事务发起方开始执行业务代码之前先调用TxManager创建事务组对象,然后拿到事务标示GroupId的过程。 2.添加事务组 添加事务组是指参与方在执行完业务方法以后,将该模块的事务信息添加通知给TxManager的操作。 3.关闭事务组 是指在发起方执行完业务代码以后,将发起方执行结果状态通知给TxManager的动作。当执行完关闭事务组的方法以后,TxManager将根据事务组信息来通知相应的参与模块提交或回滚事务。 事务控制原理 LCN事务控制原理是由事务模块TxClient下的代理连接池与TxManager的协调配合完成的事务协调控制。 TxClient的代理连接池实现了javax.sql.DataSource接口,并重写了close方法,事务模块在提交关闭以后TxClient连接池将执行"假关闭"操作,等待TxManager协调完成事务以后在关闭连接。 对于代理连接池的优化 自动超时机制,任何通讯都有最大超时限制,参与模块在等待通知的状态下也有最大超时限制,当超过时间限制以后事务模块将先确认事务状态,然后再决定执行提交或者回滚操作,主要为了给最大资源占用时间加上限制。 智能识别创建不同的连接 对于只读操作、非事务操作LCN将不开启代理功能,返回本地连接对象,对于补偿事务的启动方将开启回滚连接对象,执行完业务以后马上回滚事务。 LCN连接重用机制 当模块在同一次事务下被重复执行时,连接资源会被重用,提高连接的使用率。 事务补偿机制 为什么需要事务补偿? 事务补偿是指在执行某个业务方法时,本应该执行成功的操作却因为服务器挂机或者网络抖动等问题导致事务没有正常提交,此种场景就需要通过补偿来完成事务,从而达到事务的一致性。 补偿机制的触发条件? 当执行关闭事务组步骤时,若发起方接受到失败的状态后将会把该次事务识别为待补偿事务,然后发起方将该次事务数据异步通知给TxManager。TxManager接受到补偿事务以后先通知补偿回调地址,然后再根据是否开启自动补偿事务状态来补偿或保存该次切面事务数据。 补偿事务机制 LCN的补偿事务原理是模拟上次失败事务的请求,然后传递给TxClient模块然后再次执行该次请求事务。 模拟场景演示 若存在事务发起方、参与方A、参与方B。调用关系图如下 那么他们正常执行业务的时序图为: 若参与方B出现异常,那么他们的业务时序图为: 若他们的调用关系是这样的情况 此时发生参与方B出现异常时他们的时序图为: 三、使用 环境: SpringBoot 2.0.

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

熟透的蜗牛

永远满怀热爱,永远热泪盈眶

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值