Seata
Seata是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。
术语
-
TC(Transaction Coordinator) -事务协调者(相当于服务端)
-
维护全局和分支事务的状态,驱动全局事务提交或回滚
-
-
TM(Transaction Manager) - 事务管理器(相当于应用,相当于各模块)
-
定义全局事务的范围:开始全局事务,提交或回滚全局事务
-
-
RM(Resource Manager) - 资源管理器(数据库)
-
管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。
-
seata分布式事务原理
以官方示例为例,
老项目正常流程:当我们采用分布式部署时,Order下单后,Account账户中扣除数据完成后,再调用Storage库存减一。
但是现在数据库分开,那么Order完成后就直接完成数据库的修改,接着到Account中扣除金额完成,返回到business中,调用storage减除库存时发现库存不足,但是前两步已经提交了。
如果不做分布式事务的话我们就要重新提供接口,在进行反向的操作,例如Account中需要将金额加上。
问题:调度事情就很多,补偿很多,以及还有可能补偿失败。
-
这里就交给Seata来做,我们来写补偿。由seata来具体操作,这就是Seata的TCC模式。
-
补偿也不由我们来写,由Seata自动分析来进行,比如update金额减少之后,发现需要重新操作,这是Seata就会自动生产sql,update金额加回来。这就是Seata中的AT模式
分布式事务的四种模式
-
AT模式:默认,简单,需要增加undo_log表,生产反向SQL,性能高。回滚后,原来没数据的,现在还是没数据
-
TCC模式:try confirm/cancel ,三个阶段代码都得自己实现,Seata只负责调度。对业务代码入侵性较强,必要时可能还要修改数据库(因为try并非真正的去扣减,而是做一个冻结的操作,比如100:有我们需要扣减一,就会冻结一个,接着在confirm中解冻,真正去扣减)
-
SAGA模式,长事务解决方案,需要程序员自己编写两阶段代码(AT模式不需要写第二阶段)
-
基于状态机实现,需要一个JSON文件,可异步执行
-
-
XA模式,XA协议是由X/Open组织提出的分布式事务处理规范,基于数据库的XA协议来实现2PC又称为XA方案,适用于强一致性的场景,比如金融、银行
Seata应用 :
选中座位后事务处理,座位表修改售卖情况sell(b库),余票详情表修改余票(b库),为会员增加购票记录(会员端的接口),更新确定订单为成功(b库)
-
引入依赖:
-
需要使用分布式事务的都增加undo表。
-
配置每个模块
-
开启分布式事务,@GlobalTransactional
注:统一异常处理:
需要判断是否为全局事务。如果不加这段member出现异常时,虽然commonResp.success=false但是接口返回码是200,business会认为调用是成功的。
配置中心
seata有自己的配置中心,需要重新配置,而springcloud中的配置仅仅作用于项目。注册中心同理。
修改配置中心与注册中心:在conf目录下的application.yml中参照application.example.yml进行配置
从配置中心中配置文件
与自己设置保持一致
配置好数据库,以及基本的表,可在script/server/db下找到mysql.sql执行。
注:AT模式会有一个全局锁,用来防止脏读,线程1的事务修改了库存,但是还没有提交事务,线程二读库存读的还是原来的库存。
在配置中心定义所有的项目所属的集群,当集群挂了可以快速换成另一个集群。
sql限制:
Seata 事务目前支持 INSERT、UPDATE、DELETE 三类 DML 语法的部分功能,这些类型都是已经经过Seata开源社区的验证。SQL 的支持范围还在不断扩大,建议在本文限制的范围内使用。如果您有意帮助社区支持更多类型的SQL,请提交PR申请。
-
不支持 SQL 嵌套
-
不支持多表复杂 SQL(自1.6.0版本,MySQL支持UPDATE JOIN语句,详情请看 )
-
不支持存储过程、触发器
-
部分数据库不支持批量更新,在使用 MySQL、Mariadb、PostgreSQL9.6+作为数据库时支持批量,批量更新方式如下以 Java 为例
// use JdbcTemplate public void batchUpdate() { jdbcTemplate.batchUpdate( "update storage_tbl set count = count -1 where id = 1", "update storage_tbl set count = count -1 where id = 2" ); } • // use Statement • public void batchUpdateTwo() { • statement.addBatch("update storage_tbl set count = count -1 where id = 1"); • statement.addBatch("update storage_tbl set count = count -1 where id = 2"); • statement.executeBatch(); • }