分布式事务
一. CAP定理
CAP定理是指导分布式环境下,分布式事务的处理一个理论:
两者只能满足其二C(Consistency): 一致性(强一致性)。
A(Availability): 可用性。
P: (Partition tolerance): 分区容忍性(允许分布式环境下,多个系统间可以失去联系)。
可用性是任何一个项目必须要保证的;分区容忍性就是项目设计成分布式项目,就已经满足这一条;所以我们牺牲的是一致性,不是说不保证事务的一致性,不是强一致性,我们需要保证的是最终的一致性。
二. BASE理论
BA(Basically Available): 基本可用,因为没有事务的强一致,在实际的场景下并不满足用户的想要的结果。
Soft state(软状态): 中间状态。
Eventually consistent:最终一致性。
三. 解决方案
3.1 RabbitMQ的分布式事务解决
3.2 二阶段提交
二阶段提交和三阶段提交都是隶属于
XA协议是DBMS的一种分布式事务的协议。
二阶段事务提交和Mysql的本身的设计是有关系的。在介绍两阶段提交之前,先必须要知道两个内容:
- redo log(重做日志),是 innodb 存储引擎所独有的。
- binlog(操作日志),是mysql服务所拥有的。
例如如下的语句:
begin
update user set name = 'xx' where id = 1;
commit;
上面的语句在执行的过程中分为两个阶段:
- 首先将要更改的信息记录在 redo log中,并标记为
prepare.- 当commit的时候,会首先将修改记录在binlog中,然后在 redo log中标记为
commit.
面试, 什么叫做两阶段提交?
两阶段提交是XA协议中用来解决分布式事务问题的一种方式,底层是通过 redo log和binlog来实现的,第一个阶段用户执行sql语句的时候,会将用户的修改记录在 redo log中,标记为prepare状态;当事务提交的时候,会将操作记录在 binlog 中,接着将 redo log 中之前标记的数据改为 commit状态。在具体的业务层实现的时候,需要一个协调者,多个事务的参与者,第一个阶段的时候,协调者会让所有的事务的参与者进入准备阶段,如果事务的参与者都准备成功了,会给协调者发送yes的状态,当协调者收到所有参与者返回的正常状态,就进入第二阶段,如果某一个事务参与者准备失败,协调者会向所有的参与者发送回滚指令;第二阶段,事务的协调者会向所有的参与者发送commit指令提交事务,最后所有的参与者如果事务执行成功,会返回一个ack确认;两阶段提交有什么问题?
- 所有的步骤都是阻塞状态和没有超时机制。
- 在第二个阶段的时候,当其中一个事务的参与者事务提交失败,可能会导致事务的不一致性。
3.3 三阶段提交
面试,没接触的。
3.4 TCC
TCC(Try Commit Cancel)应该业界出现比较早的分布式事务解决方案。预检查阶段,提交,取消阶段。
- 对业务的侵入性太强,要提供给TCC框架接口,供其调用。
- 实现难度特别大。
3.5 SAGA
SAGA是TCC的改进版,少了一个Try阶段。
3.6 Seata(AT事务)
AT模式是阿里
Seata这个框架提出的分布式事务的概念。
四. Seata的搭建
4.1 数据库的创建
4.1.1 seata端数据库创建
一. 创建一个数据库,名字叫
seata
二. 执行sql语句:https://gitee.com/seata-io/seata/blob/develop/script/server/db/mysql.sql
4.1.2 业务端创建数据
在所有的业务数据库中都要执行SQL语句:https://gitee.com/seata-io/seata/blob/develop/script/client/at/db/mysql.sql
4.2 修改配置
修改
SEATA_HOME/config/registry.conf文件。
registry {
# file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
# 第三行位置,默认值 file, 将其改为 nacos
type = "nacos"
nacos {
application = "seata-server"
serverAddr = "127.0.0.1:8848"
group = "SEATA_GROUP"
namespace = ""
cluster = "default"
username = ""
password = ""
}
eureka {
serviceUrl = "http://localhost:8761/eureka"
application = "default"
weight = "1"
}
config {
# file、nacos 、apollo、zk、consul、etcd3
# 第59行,默认值为 file, 改为 nacos
type = "nacos"
nacos {
serverAddr = "127.0.0.1:8848"
namespace = ""
group = "SEATA_GROUP"
username = ""
password = ""
dataId = "seataServer.properties"
}
consul {
serverAddr = "127.0.0.1:8500"
aclToken = ""
}
4.3 seata连接数据库
修改
SEATA_HOME/config/file.conf文件。
## transaction log store, only used in seata-server
store {
## store mode: file、db、redis
# 将mode改为db就行了
mode = "db"
## rsa decryption public key
publicKey = ""
## file store property
file {
## 如下的配置,视情况而定
db {
## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp)/HikariDataSource(hikari) etc.
datasource = "druid"
## mysql/oracle/postgresql/h2/oceanbase etc.
dbType = "mysql"
driverClassName = "com.mysql.jdbc.Driver"
## if using mysql to store the data, recommend add rewriteBatchedStatements=true in jdbc connection param
url = "jdbc:mysql://127.0.0.1:3306/seata?rewriteBatchedStatements=true"
user = "root"
password = "123456"
minConn = 5
maxConn = 100
globalTable = "global_table"
branchTable = "branch_table"
lockTable = "lock_table"
queryLimit = 100
maxWait = 5000
}
4.4 启动seata
在 window 环境下双击
SEATA_HOME/bin/seata-server.bat
4.5 配置seata的信息
配置信息的地址:https://gitee.com/seata-io/seata/blob/develop/script/config-center/config.txt
将配置信息添加到 nacos 的配置项中。
service.vgroupMapping.my_test_tx_group=default
store.db.datasource=druid
store.db.dbType=mysql
store.db.driverClassName=com.mysql.jdbc.Driver
store.db.url=jdbc:mysql://127.0.0.1:3306/seata?useUnicode=true&rewriteBatchedStatements=true
store.db.user=root
store.db.password=123456
store.db.minConn=5
store.db.maxConn=30
store.db.globalTable=global_table
store.db.branchTable=branch_table
store.db.queryLimit=100
store.db.lockTable=lock_table
store.db.maxWait=5000

4.6 项目中使用
4.6.1 引入依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>
4.6.2 配置
在所有的事务参与方的配置文件中加上如下的配置:
server:
port: 8083
spring:
datasource:
username: root
password: 123456
url: jdbc:mysql://localhost:3306/test?serverTimezone=Asia/Shanghai
driver-class-name: com.mysql.cj.jdbc.Driver
hikari:
# 最小空闲的连接的数量
minimum-idle: 2
# 最大的数量, mysql的最大连接数量 1000 左右
maximum-pool-size: 100
application:
name: seata-user
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
register-enabled: true
# 如下都是seata的配置
seata:
tx-service-group: my_test_tx_group
# 表示我们的业务寻找seata的地址
registry:
type: nacos
nacos:
server-addr: 127.0.0.1:8848
# 我们的业务代码到nacos拉去我们seata的配置
config:
type: nacos
nacos:
server-addr: 127.0.0.1:8848
group: DEFAULT_GROUP
4.6.3 配置分布式事务
在事务的发起方加上
@GlobalTransactional(rollbackFor = Exception.class)就可以了。
4.7 AT模式
AT模式是 Seata 独创的,不是业界通用的事务解决方案。它中间涉及到三方来共同完成 AT 模式:
- TC(Transaction Coordinate): 事务的协调者,就是 seata 服务器。
- TM(Transaction Manager): 事务的管理者,是在事务的发起方创建的。
- RM (Resource Manager): 资源管理者。
说一下AT模式?
AT模式Seata框架提出的,它包含三个角色
事务的协调者(Seata的服务器)、事务的管理者(存在于事务的发起方)、资源管理者(分支事务)。当我们发起一个分布式事务,首先会创建一个事务的管理者, 会上报给事务的协调者,事务的协调者会下发一个XID, 当事务的管理者收到 XID, 就会创建一个全局事务,在这个事务的调用链路过程中,会在http请求头中携带XID,接着每个资源管理者执行分支事务的时候,会向事务的协调者上报分支事务的执行状况,同时会将事务执行之前的数据做一个快照,保存在undo_log表中,当分支事务执行之后,会将执行之后的数据与之前数据进行合并,当所有的分支事务执行结束后,那么就由事务的管理者向事务协调者请求提交或者回滚事务。