Seata分并式事务是阿里去年三月开源的框架,虽然还不太成熟,但是现在很多企业正在逐步使用,成熟稳定只是时间的问题。
特点是:效率高;原因是:生成反SQL(事务回滚时执行)、以空间换时间的思路。
seata 的 AT 模式,采用的是大量运用在数据库软件的 Write Ahead Log 思想,即把事务的信息以事务日志的方式记录下来。
这种处理方式,实际上是对传统两阶段提交的一种改进和优化。
1、主要有几个关键点:
传统两阶段提交协议是阻塞协议,性能差;
传统两阶段提交协议高可用性不好 ;
传统两阶段提交协议的全局事务隔离机制不支持 ;
根据八二原则,80% 的涉及到全局事务的业务是能正常完成并提交的。 因此,在 AT 模式下,seata 采取的做法是,一个事务分支的数据库操作执行完后,马上进行本地事务的提交,从而释放相关的数据库 资源。
分支事务中数据的
本地锁
由本地事务管理,在分支事务
Phase1
结束时释放。 同时,随着本地事务结束,连接 也得以释放。
分支事务中数据的 全局锁
在事务协调器侧管理,在决议
Phase2
全局提交时,全局锁马上可以释放。只有在决议全 局回滚的情况下,全局锁
才被持有至分支的
Phase2
结束。
2、
本地事务执行流程
在进行本地提交的前提是,seata 会解析 SQL,获取数据库表的元数据,根绝 SQL 类型,选择性地生成数据的前置镜像和后置镜像, 保存在 undo_log 表中,并且要求与保存 undo_log 与业务 SQL 在同一个本地事务内。
这就保证了:
如果一个本地事务被提交,那么必定对应着相应的 undo_log
如果保存 undo_log 保存失败,那么业务 SQL 也会失败
3、
全局事务提交流程
因为每个分支事务的本地事务都已经被提交,所以如果全局事务能够顺利进行到“提交“这一阶段,那么意味着所有事务分支的本地事 务都已经被提交了,数据的一致性已经得到了保证。
这个时候全局事务的提交就变得十分轻量级,就是把 undo_log 对应的记录删掉即可,即使是当时删除失败了,也已经不会影响全局事 务的最终结果,这次删不了,那就待会再删,程序删不了,没事,顶多人工删
4、
全局事务回滚流程
如果全局事务的任何一个事务分支失败了,那么全局事务就进入“回滚“流程,回滚时依据先前保存好数据镜像,将原来的数据回放回 去。如果全局回放成功,那么数据的一致性也就得到了保证,如果回放不成功,那么事务就进入异常。应对异常,可能需要重试,可能需要 人工介入
5、实战代码:首先创建一个服务管理器,用来管理事务,win环境执行:seata-server.bat,linux执行:seata-server.sh,如图:
6、客户端引入jar
<!--seata事务--> <dependency> <groupId>io.seata</groupId> <artifactId>seata-all</artifactId> <version>0.9.0</version> </dependency>
7、多个客户端配置:
8、客户端代码:主要是加注解 @GlobalTransactional
import com.alibaba.dubbo.config.annotation.Reference;
import com.enjoy.service.ReceiveService;
import com.enjoy.service.TransferService;
import io.seata.spring.annotation.GlobalTransactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
@org.springframework.stereotype.Service
public class DubboTransferService implements TransferService{
@Autowired
private JdbcTemplate jdbcTemplate;
@Reference
private ReceiveService receiveService;
@GlobalTransactional //分布式注解
public String transfer(int money) {
int resultUser = jdbcTemplate.update("INSERT INTO bank_a(money,user_name)VALUES (?,?)",-money,"nandao");
receiveService.receiveMoney(money);//这个微服务的实现方法上也要加注解 @GlobalTransactional
if (money > 20){
throw new RuntimeException("money too large");
}
return resultUser+"-";
}
}
加入事务组的流程和LCN原理一样,执行时客户端均生成一个反SQL存在数据库,如果最终commit成功提交,则反SQL会被删除;如果事务回滚,则执行反SQL。锁表的时间变少,这样间接提高了性能!