有道无术,术尚可求,有术无道,止于术。
本系列Seata 版本 2.0.0
本系列Spring Boot 版本 3.2.0
本系列Spring Cloud 版本 2023.0.0
源码地址:https://gitee.com/pearl-organization/study-seata-demo
文章目录
- 1. 概述
- 2. 存储模式
- 2.1 文件
- 2.2 数据库
- 2.3 Redis
- 2.4 Raft
- 3. 选型方案
- 3.1 高可用
- 3.2 一致性
- 3.3 性能
- 3.4 部署运维成本
- 3.5 成熟度
- 3.6 总结
1. 概述
TC
(服务端)作为事务协调者,负责维护全局和分支事务的状态,驱动全局事务提交或回滚。在一次全局事务(分布式事务)会话中,TC
需要维护一些会话数据(事务状态和记录),比如全局事务信息、分支事务信息、锁信息等。
TC
端的作用是保证事务的二阶段被正确执行,这取决于事务记录的正确存储。为确保事务记录不丢失,需要在保持状态正确的前提下,驱动所有的RM
执行正确的二阶段行为。
2. 存储模式
Seata
服务端支持多种方式存储会话数据:
file
:本地文件(不支持HA
)db
:数据库(支持HA
)redis
:缓存数据库(支持HA
)raft
:服务端Raft
算法实现(支持HA
)
模式类型配置说明:
配置项 | 描述 | 备注 | 版本说明 |
---|---|---|---|
tore.mode | 存储方式 | file 、db 、redis 、raft | 1.5.1 版本改用 lock 和 session 分离存储,2.0.0 开始支持raft 模式 |
store.lock.mode | 事务锁信息存储方式 | file 、db 、redis ,配置为空时,取 store.mode 配置项值,raft 模式不允许单独指定 | 1.5.1 版本新增,session 和 lock 可分离存储 |
store.session.mode | 事务会话信息存储方式 | file 、db 、redis ,配置为空时,取 store.mode 配置项值,raft 模式不允许单独指定 | 1.5.1 版本新增,session 和lock 可分离存储 |
store.publicKey | db 或 redis 存储密码解密公钥 | 1.4.2 版本支持 |
在Nacos
控制台配置中心seata.properties
文件中,可以看到默认使用的是file
存储模式:
2.1 文件
file
模式将会话数据存储在本地文件,以顺序写的形式将事务信息存储到本地磁盘上。为了兼顾性能,默认采用异步方式,并将事务信息存储在内存中,确保内存和磁盘上的数据一致性。当TC
意外宕机时,在重新启动时会从磁盘读取事务信息并恢复到内存中,以便继续运行事务上下文。
file
模式速度快,但是无法实现会话共享,所以不支持集群模式。
file
模式下配置说明:
配置项 | 描述 | 备注 |
---|---|---|
store.file.dir | 存储文件夹名 | 默认 sessionStore |
store.file.maxBranchSessionSize | 分支 session 最大字节数 | 默认 16384(16kb) ,单位 byte |
store.file.maxGlobalSessionSize | 全局 session 最大字节数 | 默认 512b ,单位 byte |
store.file.fileWriteBufferCacheSize | buffer 最大缓存大小 | 默认 16384(16kb) ,单位 byte ,写入 session 等数据量大于该值时会抛出异常 |
store.file.flushDiskMode | 刷盘策略 | 默认 async ,可选 sync |
store.file.sessionReloadReadSize | Server 节点重启后从备份文件中恢复的 session 或 lock key 上限个数 | 默认100 |
配置文件内容如下:
可以看到,在执行全局事务后,会在seata\bin\file_store\data\8091
文件夹下生成一个root.data
文件:
2.2 数据库
db
模式采用数据库存储会话信息,依赖于数据库,在数据库中进行事务信息的增删改查操作。一致性由数据库的本地事务保证,数据也由数据库负责持久化到磁盘。基于计算和存储分离的架构设计。
db
模式支持mysql
、oracle
、db2
、sqlserver
、sybaee
、h2
、sqlite
、access
、postgresql
、oceanbase
等数据库。支持集群,但是需要同步数据结构,依赖数据库,性能也不太高。
将存储模式改为db
:
db
模式下配置说明:
配置项 | 描述 | 备注 |
---|---|---|
store.db.datasource | 数据源类型 | 支持dbcp 、druid 、hikari ,无默认值 |
store.db.dbType | 数据库类型 | mysql 、oracle 、db2 、sqlserver 、sybaee 、h2 、sqlite 、access 、postgresql 、oceanbase ,无默认值 |
store.db.driverClassName | 数据库驱动 | |
store.db.url | 数据库 url | 在使用 mysql 作为数据源时,建议在连接参数中加上rewriteBatchedStatements=true (批量插入全局锁) |
store.db.user | 数据库账户 | |
store.db.password | 数据库账户密码 | |
store.db.minConn | 数据库初始连接数 | 默认 1 |
store.db.maxConn | 数据库最大连接数 | 默认 20 |
store.db.maxWait | 获取连接时最大等待时间 | 默认 5000 ,单位毫秒 |
store.db.globalTable | 全局事务表名 默认 global_table | |
store.db.branchTable | 分支事务表名 | 默认 branch_table |
store.db.lockTable | 全局锁表名 默认 lock_table | |
store.db.queryLimit | 查询全局事务一次的最大条数 | 默认 100 |
store.db.distributedLockTable | Sever 端事务管理全局锁存储表名 | 默认 distributed_lock ,多 Sever 集群下保证同时只有一个 Sever 处理提交或回滚,1.5.1 版本新增 |
配置文件内容如下:
db
模式下需要创建以下四张表:
global_table
:全局事务表branch_table
:分支事务表lock_table
:全局锁表distributed_lock
:Sever
事务管理全局锁存储表
在seata\script\server\db
目录提供了相关脚本:
官网SQL
脚本没有注释,最好使用作者提供的:
-- seata_server.branch_table definition
CREATE TABLE `branch_table` (
`branch_id` bigint NOT NULL COMMENT '分支事务ID',
`xid` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '全局事务唯一标识',
`transaction_id` bigint DEFAULT NULL COMMENT '全局事务ID',
`resource_group_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '资源分组ID',
`resource_id` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '资源ID',
`branch_type` varchar(8) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '事务模式',
`status` tinyint DEFAULT NULL COMMENT '分支事务状态(io.seata.core.model.BranchStatus)',
`client_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '客户端ID',
`application_data` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '应用数据',
`gmt_create` datetime(6) DEFAULT NULL COMMENT '创建时间',
`gmt_modified` datetime(6) DEFAULT NULL COMMENT '修改时间',
PRIMARY KEY (`branch_id`),
KEY `idx_xid` (`xid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='分支事务会话表';
-- seata_server.global_table definition
CREATE TABLE `global_table` (
`xid` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '全局事务的唯一标识(IP+事务ID)',
`transaction_id` bigint DEFAULT NULL COMMENT '全局事务ID',
`status` tinyint NOT NULL COMMENT '全局事务状态(io.seata.core.model.GlobalStatus)',
`application_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '应用ID(TM服务名)',
`transaction_service_group` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '事务分组名',
`transaction_name` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '开启事务类方法',
`timeout` int DEFAULT NULL COMMENT '超时时间',
`begin_time` bigint DEFAULT NULL COMMENT '开始时间',
`application_data` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '应用数据',
`gmt_create` datetime DEFAULT NULL COMMENT '创建时间',
`gmt_modified` datetime DEFAULT NULL COMMENT '修改时间',
PRIMARY KEY (`xid`),
KEY `idx_status_gmt_modified` (`status`,`gmt_modified`),
KEY `idx_transaction_id` (`transaction_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='全局事务会话表';
-- seata_server.lock_table definition
CREATE TABLE `lock_table` (
`row_key` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '行键',
`xid` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '全局事务唯一标识',
`transaction_id` bigint DEFAULT NULL COMMENT '全局事务ID',
`branch_id` bigint NOT NULL COMMENT '分支ID',
`resource_id` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '资源ID',
`table_name` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '表名',
`pk` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '主键对应的值',
`status` tinyint NOT NULL DEFAULT '0' COMMENT '0:locked ,1:rollbacking锁状态(0,已加锁;1,回滚中)',
`gmt_create` datetime DEFAULT NULL COMMENT '创建时间',
`gmt_modified` datetime DEFAULT NULL COMMENT '修改时间',
PRIMARY KEY (`row_key`),
KEY `idx_status` (`status`),
KEY `idx_branch_id` (`branch_id`),
KEY `idx_xid` (`xid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='全局锁信息表';
-- seata_server.distributed_lock definition
CREATE TABLE `distributed_lock` (
`lock_key` char(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '锁键',
`lock_value` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '锁值',
`expire` bigint DEFAULT NULL COMMENT '过期时间',
PRIMARY KEY (`lock_key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='Sever事务管理全局锁存储表';
INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('AsyncCommitting', ' ', 0);
INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('RetryCommitting', ' ', 0);
INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('RetryRollbacking', ' ', 0);
INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('TxTimeoutCheck', ' ', 0);
2.3 Redis
redis
模式采用缓存数据库redis
会话信息,是Seata 1.4
以后支持的新模式。和db
类似,也是一种事务存储方式,利用 Jedis
和 Lua
脚本来进行事务的增删改查操作,部分操作(如竞争锁)在 Seata 2.x
版本中全部采用了Lua
脚本。也依赖于存储方Redis
来保证数据的一致性。基于计算和存储分离的架构设计。
redis
模式的优点是支持集群,效率较快, 无需要同步数据结构。
将存储模式改为redis
:
db
模式下配置说明:
配置项 | 描述 | 备注 | 版本说明 |
---|---|---|---|
store.redis.mode | 部署模式 | 默认 single ,可选 sentinel | 1.4.2 版本新增 sentinel 模式 |
store.redis.single.host | 主机地址 | 单机模式下 redis 的 host ,兼容 1.4.2 之前的版本,该配置为空时选取 store.redis.host 作为配置项 | 1.4.2 版本新增 |
store.redis.single.port | 端口 | 单机模式下 redis 的 port ,兼容 1.4.2 之前的版本,该配置为空时选取 store.redis.port 作为配置项 | 1.4.2 版本新增 |
store.redis.sentinel.masterName | sentinel 模式下 redis 的主库名称 | 1.4.2 版本新增 | |
store.redis.sentinel.sentinelHosts | sentinel 模式下 sentinel 的 host | 多 hosts 以逗号分隔 | 1.4.2 版本新增 |
store.redis.host | ip | 默认 127.0.0.1 | 1.4.2 版本弃用 |
store.redis.port | 端口 | 默认 6379 | 1.4.2 版本弃用 |
store.redis.maxConn | 最大连接数 | 默认 10 | |
store.redis.minConn | 最小连接数 | 默认 1 | |
store.redis.database | 默认库 | 默认 0 | |
store.redis.password | 密码(无可不填) | 默认 null | |
store.redis.queryLimit | 一次查询最大条数 | 默认 100 | |
store.redis.type | 主要使用的方式: lua 、pippline | pippline |
2.4 Raft
Raft官网地址
Seata-Raft 存储模式详解及入门
Raft
是一种用于分布式场景下的一致性算法,Seata 2.0
开始支持,提供了存算一体的高性能易扩展,入门门槛低,运维成本低等特定的事务存储模式。
Seata-Raft
模式的设计思路是通过封装无法高可用的file
模式,利用Raf
t算法实现多个TC
之间数据的同步。该模式保证了使用file
模式时多个TC
的数据一致性,同时将异步刷盘操作改为使用Raft
日志和快照进行数据恢复。
注意:
- 😶😶😶但是目前基于
Raft
模式的解决方案可能还不够成熟,生产环境切勿使用。😶😶😶 - 🤫🤫🤫不支持与第三方注册中心搭配,故全链路只允许存在一个
TC
集群🤫🤫🤫
将存储模式改为raft
:
raft
模式下配置说明:
配置项 | 描述 | 备注 | 版本说明 |
---|---|---|---|
server.raft.group | 分组,client 的事务分组对应的值要与之对应,如 service.vgroup-mapping.default_tx_group=default | 默认default | 2.0.0 版本新增 |
server.raft.server-addr | 集群列表 | 2.0.0 版本新增 | |
server.raft.snapshot-interval | 间隔多久做一次内存快照,每做一次快照将暂停状态机,但是能提高停机恢复速度 | 默认 600 秒 | 2.0.0 版本新增 |
server.raft.apply-batch | 任务累积批次后提交至 leader | 默认 32 | 2.0.0 版本新增 |
server.raft.max-append-bufferSize | raft 日志存储缓冲区最大大小 | 默认 256K | 2.0.0 版本新增 |
server.raft.max-replicator-inflight-msgs | 在启用 pipeline 请求情况下,最大 in-flight 请求数 | 默认 25 6 | 2.0.0 版本新增 |
server.raft.disruptor-buffer-size | 内部 disruptor buffer 大小,如果是写入吞吐量较高场景,需要适当调高该值 | 默认 16384 | 2.0.0 版本新增 |
server.raft.election-timeout-ms | 超过多久没有 leader 的心跳开始重选举 | 默认 1000 毫秒 | 2.0.0 版本新增 |
server.raft.reporter-enabled | raft 自身的监控是否开启 | 默认 false | 2.0.0 版本新增 |
server.raft.reporter-initial-delay | 监控输出间隔 | 默认 60 秒 | 2.0.0 版本新增 |
server.raft.serialization | 序列化方式,目前仅支持 jackson | 默认 jackson | 2.0.0 版本新增 |
server.raft.compressor | raftlog 和 snapshot 的压缩方式,支持 gzip, zstd, lz4 | none | 2.0.0 版本新增 |
server.raft.sync | raftlog 同步刷盘 | true | 2.0.0 版本新增 |
配置文件内容如下:
Seata
服务端需要修改注册中心为file
:
seata:
config:
# support: nacos, consul, apollo, zk, etcd3
type: nacos# 该配置可以选择不同的配置中心
registry:
# support: nacos, eureka, redis, zk, consul, etcd3, sofa
type: file # raft模式下不允许使用非file的其他注册中心
store:
# support: file 、 db 、 redis 、 raft
mode: raft # 使用raft存储模式
file:
dir: sessionStore # 该路径为raftlog及事务相关日志的存储位置,默认是相对路径,最好设置一个固定的位置
Seata
客户端事务分组对应的TC
集群需要修改为和server.raft.group
一致,并修改注册中心类型为raft
:
将tc集群改为server的raft group
seata:
tx-service-group: default_tx_group
service:
vgroup-mapping:
default_tx_group: 如果server.raft.group为default,那么此处便是default
registry:
type: raft
raft:
server-addr: 192.168.0.111:7091, 192.168.0.112:7091, 192.168.0.113:7091
3. 选型方案
Seata
当前已经趋于成熟,性能、入门门槛、部署运维成本是未来发展的重点方向。
3.1 高可用
除了file
模式外,db
、redis
、raft
模式都支持高可用集群部署。
3.2 一致性
一致性排名如下(由高到低):
raft
:基于raft
一致性算法db
:基于数据库事务file
:基于异步刷盘redis
:基于RDB
和AOF
持久化
3.3 性能
性能排名如下(由高到低):
file
:基于直接内存redis
:基于内存数据库raft
:基于raft
db
:基于数据库
3.4 部署运维成本
部署运维成本排名如下(由高到低):
file
:默认方式,直接部署即可raft
:只需修改配置,无需注册中心redis
:依赖于内存数据库,生产环境需要部署注册中心db
:基于数据库,需要同步数据接口,生产环境主要部署注册中心
3.5 成熟度
除了新出的raft
模式,其他的基本已经很成熟,生产可用。
3.6 总结
raft
模式是最理想的方案,但是当前并不成熟,所以不用考虑。
file
模式简单易用,但是只支持单机模式部署,生产环境不考虑。
redis
和db
模式都支持集群部署,redis
更易使用,且性能更高,所以推荐redis
模式。