一、简介
Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。
在 Seata 开源之前,Seata 对应的内部版本在阿里经济体内部一直扮演着分布式一致性中间件的角色,帮助经济体平稳地度过历年的双 11,对各 BU 业务进行了有力的支撑。经过多年沉淀与积累,商业化产品先后在阿里云、金融云进行售卖。2019 年 1 月为了打造更加完善的技术生态和普惠技术成果,Seata 正式宣布对外开源,未来 Seata 将以社区共建的形式帮助其技术更加可靠与完备。
官网:
术语
TC (Transaction Coordinator) - 事务协调者
维护全局和分支事务的状态,驱动全局事务提交或回滚。
TM (Transaction Manager) - 事务管理器
定义全局事务的范围:开始全局事务、提交或回滚全局事务。
RM (Resource Manager) - 资源管理器
管理分支事务处理的资源,与 TC 交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。
事务模式
Seata 提供了 XA、AT、TCC 与 SAGA 四种分布式事务模式
XA 模式
前提:
- 支持 XA 事务的数据库。
- Java 应用,通过 JDBC 访问数据库。
整体机制:
在 Seata 定义的分布式事务框架内,利用事务资源(数据库、消息服务等)对 XA 协议的支持,以 XA 协议的机制来管理分支事务的一种 事务模式。
- 执行阶段:
- 可回滚:业务 SQL 操作放在 XA 分支中进行,由资源对 XA 协议的支持来保证可回滚
- 持久化:XA 分支完成后,执行 XA prepare,同样,由资源对 XA 协议的支持来保证持久化(即,之后任何意外都不会造成无法回滚的情况)
- 完成阶段:
- 分支提交:执行 XA 分支的 commit
- 分支回滚:执行 XA 分支的 rollback
问题:
- 回滚日志无法自动清理,需要手工清理。
- 多线程下对同一个 RM 中的数据进行修改,存在 ABA 问题。
AT 模式
前提:
- 基于支持本地 ACID 事务的关系型数据库。
- Java 应用,通过 JDBC 访问数据库。
整体机制:
两阶段提交协议的演变:
- 一阶段:业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源。
- 二阶段:
- 提交异步化,非常快速地完成。
- 回滚通过一阶段的回滚日志进行反向补偿。
问题:
- 不支持 NoSQL。
- 全局 commit/rollback 阶段及回滚日志的清除过程,完全“自动化”无法实现定制化过程。
TCC 模式
AT 模式基于支持本地 ACID 事务 的关系型数据库:
- 一阶段 prepare 行为:在本地事务中,一并提交业务数据更新和相应回滚日志记录。
- 二阶段 commit 行为:马上成功结束,自动 异步批量清理回滚日志。
- 二阶段 rollback 行为:通过回滚日志,自动 生成补偿操作,完成数据回滚。
相应的,TCC 模式,不依赖于底层数据资源的事务支持:
- 一阶段 prepare 行为:调用自定义 的 prepare 逻辑。
- 二阶段 commit 行为:调用自定义 的 commit 逻辑。
- 二阶段 rollback 行为:调用自定义 的 rollback 逻辑。
所谓 TCC 模式,是指支持把自定义 的分支事务纳入到全局事务的管理中。
TCC,Try Confirm/Cancel,同样也是 2PC 的,其与 AT 的重要区别是,支持将自定义的分支事务纳入到全局事务管理中,即可以实现定制化的日志清理与回滚过程。当然,该模式对业务逻辑的侵入性是较大的。
Sage 模式
对于架构复杂,且业务流程较多较长的系统,一般不适合使用 2PC 的分布式事务模式。因为这种系统一般无法提供 TM、TC、RM 三种接口。此时,我们可以尝试着选择 Saga 模式
Saga 模式是 SEATA 提供的长事务解决方案,在 Saga 模式中,业务流程中每个参与者都提交本地事务,当出现某一个参与者失败则补偿前面已经成功的参与者,一阶段正向服务和二阶段补偿服务都由业务开发实现。
其应用场景是:在无法提供 TC、TM、RM 接口的情况下,对于一个流程很长的复杂业务,其会包含很多的子流程(事务)。每个子流程都让它们真实提交它们真正的执行结果。
只有当前子流程执行成功后才能执行下一个子流程。若整个流程中所有子流程全部执行成功,则整个业务流程成功;只要有一个子流程执行失败,则可采用两种补偿方式:
- 向后恢复:对于其前面所有成功的子流程,其执行结果全部撤销
- 向前恢复:重试失败的子流程直到其成功。当然这个前提是要确保每个分支事务都能够成功。
与2PC模式的区别
Saga 模式的所有分支事务是串行执行的,而 2PC 的则是并行执行的。
Saga 模式没有 TC,其是通过子流程间的消息传递来完成全局事务管理的,而 2PC 则具有 TC,其是通过 TC 完成全局事务管理的。
说明
无论是 AT 模式,还是 TCC 模式或 XA 模式,都需要有事务协调器 TC,即 Seata Server
下载地址:
Seata 的源码包与二进制包均需要下载。因为 Seata Client 需要使用 Seata 的源码包中的一个 sql 脚本文件。
二、Seata Server 存储模式
Seata Server 需要对全局事务与分支事务进行存储,以便对它们进行管理。其存储模式目前支持三种:file、db 与 redis。
- file 模式:会将相关数据存储在本地文件中,一般用于 Seata Server 的单机测试。
- db 模式:会将相关数据存储在数据库中,一般用于生产环境下的 Seata Server 集群部署。生产环境下使用最多的模式。
- redis 模式:会将相关数据存储在 redis 中,一般用于生产环境下的 Seata Server 集群部署。性能略高于 db 模式,如果对性能要求较高,可选择 redis 模式。
1、file 模式
①修改application.xml
将 seata-server-1.7.1.zip 解压后,修改 seata 解压目录下的 conf 目录中的 application.yml文件。默认file模式,在该文件中需要配置三类信息:Seata 的配置中心、Seata 的注册中心,及回滚日志信息。不过,对于 file 模式,只需要修改以下位置即可。
seata:
config:
# support: nacos, consul, apollo, zk, etcd3
type: file
registry:
# support: nacos, eureka, redis, zk, consul, etcd3, sofa
type: file
store:
# support: file 、 db 、 redis
mode: file
②启动Seata-Server
在seata/bin
目录下直接双击seata-server.sh
批处理文件即可启动。启动后在该 bin 目录中就可以直接看到生成的 sessionStore 目录及其中的 root.data 文件。
2、db 模式
file 模式一般用于简单测试,生产环境下使用的是 db 模式
①运行mysql.sql脚本
在 seata 包解压目录的 script/server/db
下找到mysql.sql
文件。该脚本文件中创建了 4张表。这 4 张表都是用于保存整个系统中分布式事务相关日志数据的。
该脚本文件中仅有建表语句,没有创建数据库的语句,说明使用什么数据库名称都可以。为了方便,我们在脚本文件中直接添加了建库语句,并指定数据库名称为 seata。
create database if not exists seata;
②修改application.xml
修改 seata 解压目录下的conf
目录中的application.yml
文件。在该文件中需要配置三类信息:Seata 的配置中心、Seata 的注册中心,及回滚日志信息。
server:
port: 7091
spring:
application:
name: seata-server
logging:
config: classpath:logback-spring.xml
file:
path: ${log.home:${user.home}/logs/seata}
extend:
logstash-appender:
destination: 127.0.0.1:4560
kafka-appender:
bootstrap-servers: 127.0.0.1:9092
topic: logback_to_logstash
console:
user:
username: seata
password: seata
seata:
config:
# support: nacos, consul, apollo, zk, etcd3
type: nacos
nacos:
server-addr: 127.0.0.1:8848
namespace:
group: SEATA_GROUP
username: nacos
password: nacos
context-path:
##if use MSE Nacos with auth, mutex with username/password attribute
#access-key:
#secret-key:
data-id: seataServer.properties
registry:
# support: nacos, eureka, redis, zk, consul, etcd3, sofa
type: nacos
nacos:
application: seata-server
server-addr: 127.0.0.1:8848
group: SEATA_GROUP
namespace:
cluster: default
username: nacos
password: nacos
context-path:
##if use MSE Nacos with auth, mutex with username/password attribute
#access-key:
#secret-key:
store:
# support: file 、 db 、 redis
mode: db
db:
datasource: druid
db-type: mysql
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/seata?rewriteBatchedStatements=true
user: root
password: 123456
min-conn: 10
max-conn: 100
global-table: global_table
branch-table: branch_table
lock-table: lock_table
distributed-lock-table: distributed_lock
query-limit: 1000
max-wait: 5000
# server:
# service-port: 8091 #If not configured, the default is '${server.port} + 1000'
security:
secretKey: SeataSecretKey0c382ef121d778043159209298fd40bf3850a017
tokenValidityInMilliseconds: 1800000
ignore:
urls: /,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.jpeg,/**/*.ico,/api/v1/auth/login
mysql5.7 使用 com.mysql.jdbc.Driver
Mysql8.0 使用 com.mysql.cj.jdbc.Driver
③修改 config.txt
修改 seata 解压目录的script/config-center
下的 config.txt
文件。这里的内容都是 key-value,将来都是作为 nacos 配置中心中的数据出现的,将来打开 nacos 可以根据其 key 逐条查看到
修改存储模式
指定这里要使用的存储模式为 db
将store.mode
、store.lock.mode
、store.session.mode
中原来的 file 值修改为 db。再将公钥行注释掉。
修改db的连接四要素
store.db.datasource=druid
store.db.dbType=mysql
store.db.driverClassName=com.mysql.cj.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
删除其它存储模式配置
由于这里指定的存储模式是db,所以需要将file模式与redis模式相关的配置全部删除。
定义seataServer.properties
在 Nacos 配置中心定义 seataServer.properties 文件。该文件是在上述 application.yml 中指定的 data-id 文件。将前面修改后的 config.txt 文件内容全部复制到 seataServer.properties文件中。
#For details about configuration items, see https://seata.io/zh-cn/docs/user/configurations.html
#Transport configuration, for client and server
transport.type=TCP
transport.server=NIO
transport.heartbeat=true
transport.enableTmClientBatchSendRequest=false
transport.enableRmClientBatchSendRequest=true
transport.enableTcServerBatchSendResponse=false
transport.rpcRmRequestTimeout=30000
transport.rpcTmRequestTimeout=30000
transport.rpcTcRequestTimeout=30000
transport.threadFactory.bossThreadPrefix=NettyBoss
transport.threadFactory.workerThreadPrefix=NettyServerNIOWorker
transport.threadFactory.serverExecutorThreadPrefix=NettyServerBizHandler
transport.threadFactory.shareBossWorker=false
transport.threadFactory.clientSelectorThreadPrefix=NettyClientSelector
transport.threadFactory.clientSelectorThreadSize=1
transport.threadFactory.clientWorkerThreadPrefix=NettyClientWorkerThread
transport.threadFactory.bossThreadSize=1
transport.threadFactory.workerThreadSize=default
transport.shutdown.wait=3
transport.serialization=seata
transport.compressor=none
#Transaction routing rules configuration, only for the client
service.vgroupMapping.default_tx_group=default
#If you use a registry, you can ignore it
service.default.grouplist=127.0.0.1:8091
service.enableDegrade=false
service.disableGlobalTransaction=false
#Transaction rule configuration, only for the client
client.rm.asyncCommitBufferLimit=10000
client.rm.lock.retryInterval=10
client.rm.lock.retryTimes=30
client.rm.lock.retryPolicyBranchRollbackOnConflict=true
client.rm.reportRetryCount=5
client.rm.tableMetaCheckEnable=true
client.rm.tableMetaCheckerInterval=60000
client.rm.sqlParserType=druid
client.rm.reportSuccessEnable=false
client.rm.sagaBranchRegisterEnable=false
client.rm.sagaJsonParser=fastjson
client.rm.tccActionInterceptorOrder=-2147482648
client.tm.commitRetryCount=5
client.tm.rollbackRetryCount=5
client.tm.defaultGlobalTransactionTimeout=60000
client.tm.degradeCheck=false
client.tm.degradeCheckAllowTimes=10
client.tm.degradeCheckPeriod=2000
client.tm.interceptorOrder=-2147482648
client.undo.dataValidation=true
client.undo.logSerialization=jackson
client.undo.onlyCareUpdateColumns=true
server.undo.logSaveDays=7
server.undo.logDeletePeriod=86400000
client.undo.logTable=undo_log
client.undo.compress.enable=true
client.undo.compress.type=zip
client.undo.compress.threshold=64k
#For TCC transaction mode
tcc.fence.logTableName=tcc_fence_log
tcc.fence.cleanPeriod=1h
#Log rule configuration, for client and server
log.exceptionRate=100
#Transaction storage configuration, only for the server. The file, db, and redis configuration values are optional.
store.mode=db
store.lock.mode=db
store.session.mode=db
#Used for password encryption
#store.publicKey=
#These configurations are required if the `store mode` is `db`. If `store.mode,store.lock.mode,store.session.mode` are not equal to `db`, you can remove the configuration block.
store.db.datasource=druid
store.db.dbType=mysql
store.db.driverClassName=com.mysql.cj.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.distributedLockTable=distributed_lock
store.db.queryLimit=100
store.db.lockTable=lock_table
store.db.maxWait=5000
#Transaction rule configuration, only for the server
server.recovery.committingRetryPeriod=1000
server.recovery.asynCommittingRetryPeriod=1000
server.recovery.rollbackingRetryPeriod=1000
server.recovery.timeoutRetryPeriod=1000
server.maxCommitRetryTimeout=-1
server.maxRollbackRetryTimeout=-1
server.rollbackRetryTimeoutUnlockEnable=false
server.distributedLockExpireTime=10000
server.xaerNotaRetryTimeout=60000
server.session.branchAsyncQueueSize=5000
server.session.enableBranchAsyncRemove=false
server.enableParallelRequestHandle=false
#Metrics configuration, only for the server
metrics.enabled=false
metrics.registryType=compact
metrics.exporterList=prometheus
metrics.exporterPrometheusPort=9898
④启动 Seata-server
运行命令
在 seata 解压目录下的 bin 目录中有个文件seata-server.sh
,在命令行运行这个批处理文件
./seata-server.sh -m db
⑤查看 nacos
在 nacos 中可以看到 seata-server 的服务时,表示 seata 启动成功了。
⑥seata web界面
端口默认7091
、账号密码:seata