目录
一、什么是事务
事务:是数据库操作的最小工作单元,是作为单个逻辑工作单元执行的一系列操作;这些操作作为一个整体一起向系统提交,要么都执行、要么都不执行;事务是一组不可再分割的操作集合(工作逻辑单元
事务的四大特性:ACID
-
Atomicity(原子性):一个事务(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被恢复(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。
-
Consistency(一致性):在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设规则,这包含资料的精确度、串联性以及后续数据库可以自发性地完成预定的工作。
- Isolation(隔离性):数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括读未提交(Read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(Serializable)。
- 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://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://www.bmabk.com/index.php/post/18537.html