Seata分布式事务解决方案详解

标签: seata 分布 | 发表时间:2022-04-29 02:35 | 作者:乔迁
出处:https://juejin.cn/tag/%E6%9E%B6%E6%9E%84

分布式事务现存方案

什么是分布式事务,这里就不做解释了,介绍一下下面的常用分布式事务解决方案

Seata分布式事务框架:阿里巴巴2019年开源的分布式事务解决方案。

AT,TCC,SAGA,XA。本文会详细分析AT和TCC原理以及对比,SAGA和XA暂时不在本文讨论中,后续会补上。提一嘴,Saga不存在并发执行问题,因为Saga本质上是一个责任链模式,在同一个线程上有严格的先后执行驱动顺序。

RocketMQ柔性事务,在国内绝大多数的互联网公司里,一般来说,重要的业务系统,一般都是使用mq柔性事务,大多情况下,一般来说都能确保数据是一致的

社区活跃度

Seata在19年初开源,一经发布,社区活跃度一路走高,因为其低侵入性的特性而大受欢迎。从开源地址来看目前seata在市面上的使用及活跃度是非常高的。

image-20220424100638950.png

Seata分布式方案实现成本分析

这里只对AT和TCC做分析

AT 模式是一种无侵入的分布式事务解决方案。在 AT 模式下,用户只需关注自己的“业务 SQL”,用户的 “业务 SQL” 作为一阶段,Seata 框架会自动生成事务的二阶段提交和回滚操作,使用成本很低,只需要安装seata server,然后在pom文件引用seata-all的包,在yml写上对应的配置即可,AT模式上游服务方法加上注解即可,适用于不希望对业务进行改造的场景,几乎 0 学习成本

但是其实这类统一的分布式事物的框架都不太稳定,所以基本上大家要么选用TCC方式去实现,要么就消息队列去保证最终一致性。但后两者都额外的增加了回归或实现最终一致性的成本,并且使用TCC方案会引出 TCC 的悬挂,幂等,空回滚等极端问题,并且TCC 对业务的侵入性很强,而且这种模式并不能很好地被复用,会导致开发量激增,这个也是TCC难用的地方。

除特定的数据强一致性场景(如金融),可能会针对事务做很多的工作。大部分项目对分布式事务的要求并不是很高,能不用尽量不用,解决分布式事务最好的方案就是不要导致分布式事务的产生。

AT和TCC模式对比

AT TCC
全局锁 需要 不需要
undo_log回滚日志 需要 不需要
commit/cancel阶段代码实现 不需要 需要
是否需要开发者解决悬挂和空回滚问题 不需要 需要
性能 低(高并发时的全局锁) 高(无锁)

Seata AT模式

Seata AT模式分布式事务原理

在每个服务对应的库里,都建好undo_log表,比如我们执行一个插入语句,seata会对应的去生成一个逆向操作的回滚日志。

我们的增删改操作和seata回滚日志的生成,会绑定在一个本地事务里提交,要么一起成功,要么一起失败。

上游服务开启一个全局事务,调用下游服务的时候,rpc调用会传递xid給下游服务,下游服务也会去注册分支事务branch_id,下游本地事务提交成功以后,会通知seata server分支事务成功

如果下游事务失败,会通知seata server分支事务失败,然后seata server会通知每个服务的seata客户端进行回滚,根据MySql里的undo log表进行逆向操作。

Seata AT模式分布式事务原理.jpg

Seata AT模式分布式事务读写隔离原理

没有全局锁就会出现数据错乱,分布式事务1回滚补偿,但是补偿的数据已经被分布式事务2篡改了。

读隔离:在全局层面,是读未提交,本地事务提交了,但是全局事务还没提交,所以是一个未提交的状态,这时候另一个分布式事务2过来了,可以查询到没有提交的分布式事务1的已经提交的本地事务更新的数据。

Seata AT模式读写隔离原理.jpg

Seata本地锁与全局锁的死锁问题以及超时机制

分布式事务1根据之前的undo log 执行逆向操作sql进行补偿,但是需要去获取11001的本地锁,但是此时本地锁被分布式事务2持有,并且它在等待获取全局锁提交事务释放本地锁,但是全局锁又被分布式事务1持有着,典型的死锁。

Seata为了解决死锁,设置了超时机制,超过一定时间还没获取到全局锁,就任务本次分布式事务失败,本地事务回滚。

全局锁的存在,导致AT模式在高并发情况下,会出现吞吐量降低的情况,比如举个例子

生单链路中,用户发起一个生单请求,上游订单服务调用下游库存服务,但是一个sku在库存表里对应一条数据,如果大量的用户同时对同一个sku发起生单请求,那么就会出现某一个库存sku id的全局锁竞争的情况了,导致吞吐量大大降低,这也是AT模式的不足之处。

TCC

TCC是业务层面的分布式事务,最终一致性,不会一直持有资源的全局锁。

try(预留一些资源,实际的业务动作并没有执行)、commit(实际的业务动作执行)、cancel(把预留的资源做一个逆向补偿,取消资源的预留)

Seata AT模式在高并发的情况下,由于全局锁的概念,可能导致吞吐量降低(超时机制保证了不会出现死锁问题)

AT模式有点像自动挡,加个注解,加一张undo_log表就不需要管了,刚性事务,要么全都成功,要么全都失败。

用不要有全局锁的分布式事务方案,TCC,手动挡,不依赖于底层数据资源的事务支持。

TCC.jpg

TCC模式可能会存在的问题

三者都只是有可能会产生,不是必然事件,但是我们代码中,需要去做防御性编程处理这三种情况,才能使我们的TCC代码足够的健壮

简单来说就是,try可能会出现一些问题,导致卡住了没好好去执行,seata server可能认为这个try没有好好去执行,让你先跑一个cancel回滚操作,这个可能性也是存在的,但是此时你这个try并没有执行,所以这个回滚,是个空回滚,然后等你空回滚完了,try又执行了,然后你的资源就被try悬挂起来了。

还有一种场景,比方说,先跑了一个try,seata server认为try已经成功了,但是其实try并没有成功,然后第二个分支事务失败了,会通知第一个分支事务回滚,这时候就会在没有try成功的情况下出现一个空回滚

悬挂

1.4.2版本之后增加了TCC防悬挂措施,需要数据源支持。

其实就是有一些资源被悬挂起来后续无法处理了

  1. 发起方通过RPC调用参与者一阶段Try,但是发生网络阻塞导致RPC超时
  2. RPC超时后,TC会回滚分布式事务(可能是发起方主动通知TC回滚或者是TC发现事务超时后回滚),调用已注册的各个参与方的二阶段Cancel
  3. 参与方空回滚后,发起方对参与者的一阶段Try才开始执行,进行资源预留从而形成悬挂

空回滚

当没有调用参与方Try方法的情况下,就调用了二阶段的Cancel方法,

Cancel方法需要有办法识别出此时Try有没有执行。如果Try还没执行,

表示这个Cancel操作是无效的,即本次Cancel属于空回滚。

如果Try已经执行,那么执行的是正常的回滚逻辑。

二阶段重试幂等

try成功了以后,seata server会通知分支事务执行commit,但是如果commit失败了,它会不断的进行重试,cancel同理。

只要有重试,那么就要保证commit,cancel幂等。

Seata AT&&TCC实现方案

AT模式

每个服务对应的库里,都要加入一张表undo_log,以提供给seata执行逆向操作

  CREATE TABLE `undo_log` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `branch_id` bigint NOT NULL COMMENT '分支事务id',
  `xid` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '全局事务id',
  `context` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '上下文',
  `rollback_info` longblob NOT NULL COMMENT '回滚日志',
  `log_status` int NOT NULL COMMENT '日志状态',
  `log_created` datetime NOT NULL COMMENT '创建时间',
  `log_modified` datetime NOT NULL COMMENT '更新时间',
  `ext` varchar(100) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;

上游服务方法加注解 @GlobalTransactional(rollbackFor = Exception.class)

下游被rpc调用的方法,加上本地spring事务注解 @Transactional(rollbackFor = Exception.class)

上游服务会去跟seata server注册全局事务xid,然后rpc调用的时候将xid传递给下游服务,下游服务获取本地锁,执行本地事务,插入undo log,向seata server申请分支事务branchId,然后申请全局锁,提交本地事务释放本地锁,通知seata server分支事务成功/失败。

下游服务分支事务全都执行成功后,上游服务向seata server提交全局事务,全局事务提交成功后,各个分支事务删除xid+branch id的undo logs。

但是假设一个请求 基于seata对一条数据加了全局锁,并进行了本地分支事务提交。此时全局事务没有提交。另一个请求过来(这个请求就是简单的更新这条数据,不是分布式事务)自然就不需要获取全局锁。也就是可以直接更新。那全局事务再进行回滚时就有问题了,这也正是我们开发时需要考虑的地方,当有可能操作同一条数据引起并发问题的时候,给他加上全局事务,去获取全局锁。

分布式事务成功截图

seata成功描述.png 分布式事务回滚截图

seata异常截图.png

TCC模式

TCC不需要undo log表,但是针对空回滚问题,可以搞一张空回滚记录表,下文会仔细讲解。

调用方 方法可以打上全局事务注解,如果是AT+TCC混合模式,TCC这里不要打全局事务注解,在AT上游打上就行了,理解成二者为一个分布式事务。

  @GlobalTransactional(rollbackFor = Exception.class)

很多时候,预留资源的try并不好做,try可以直接做成实际业务操作,比如直接锁库存。

在 TCC interface接口层,写一个注解

  @LocalTCC

代表这是本地TCC接口

搞三个接口,try(预留一些资源,实际的业务动作并没有执行)、commit(实际的业务动作执行)、cancel(把预留的资源做一个逆向补偿,取消资源的预留)。

  @TwoPhaseBusinessAction(name = "tccInterfaceService", commitMethod = "commitMethodName", rollbackMethod = "rollbackMethodName")

try方法,上面打这个注解,name写你接口交给spring容器管理的bean名,commitMethod写上第二个commit方法名字,rollbackMethod写上执行cancel回滚方法的名字。

极端问题解决方案

上面已经知道,TCC方案会有极端情况出现(悬挂、空回滚、二阶段重试幂等),如何解决呢,我这边引用了TccResultHolder,存储TCC第一阶段执行结果,用于解决这些问题。

我们定义一个类 TccResultHolder,存储TCC第一阶段执行结果,用于解决TCC幂等,空回滚,悬挂问题。

类里面搞俩常量,标识TCC try阶段开始执行的标识以及标识TCC try阶段执行成功的标识,再定义一个ConcurrentHashMap用来保存TCC事务执行过程的状态。

      /**
     * 标识TCC try阶段开始执行的标识
     */
    private static final String TRY_START = "TRY_START";

    /**
     * 标识TCC try阶段执行成功的标识
     */
    private static final String TRY_SUCCESS = "TRY_SUCCESS";

    /**
     * 保存TCC事务执行过程的状态
     */
    private static Map<Class<?>, Map<String, String>> map =
            new ConcurrentHashMap<Class<?>, Map<String, String>>();

tagTryStart方法和tagTrySuccess方法用于标记try阶段开始执行和try阶段执行成功,把执行tcc的实现类,业务唯一标识(如sku),全局事务xid(通过BusinessActionContext获取xid)传进来即可。

  /**
 * 标记try阶段开始执行
 *
 * @param tccClass 执行tcc的实现类
 * @param bizKey   业务唯一标识
 * @param xid      全局事务xid
 */
public static void tagTryStart(Class<?> tccClass, String bizKey, String xid) {
    setResult(tccClass, bizKey, xid, TRY_START);
}
/**
 * 标记try阶段执行成功
 *
 * @param tccClass 执行tcc的实现类
 * @param xid      全局事务xid
 */
public static void tagTrySuccess(Class<?> tccClass, String bizKey, String xid) {
    setResult(tccClass, bizKey, xid, TRY_SUCCESS);
}
 /**
     * 判断标识是否为空
     *
     * @param tccClass
     * @param xid
     * @return
     */
    public static boolean isTagNull(Class<?> tccClass, String bizKey, String xid) {
        String v = getResult(tccClass, bizKey, xid);
        if (StringUtils.isBlank(v)) {
            return true;
        }
        return false;
    }
    /**
     * 判断try阶段是否执行成功
     *
     * @param tccClass
     * @param xid
     * @return
     */
    public static boolean isTrySuccess(Class<?> tccClass, String bizKey, String xid) {
        String v = getResult(tccClass, bizKey, xid);
        if (StringUtils.isNotBlank(v) && TRY_SUCCESS.equals(v)) {
            return true;
        }
        return false;
    }

public static void setResult(Class<?> tccClass, String bizKey, String xid, String v) {
        Map<String, String> results = map.get(tccClass);

        if (results == null) {
            synchronized (map) {
                if (results == null) {
                    results = new ConcurrentHashMap<>();
                    map.put(tccClass, results);
                }
            }
        }
        //保存当前分布式事务id
        results.put(getTccExecution(xid, bizKey), v);
    }

    public static String getResult(Class<?> tccClass, String bizKey, String xid) {
        Map<String, String> results = map.get(tccClass);
        if (results != null) {
            return results.get(getTccExecution(xid, bizKey));
        }

        return null;
    }


    public static void removeResult(Class<?> tccClass, String bizKey, String xid) {
        Map<String, String> results = map.get(tccClass);
        if (results != null) {
            results.remove(getTccExecution(xid, bizKey));
        }
    }
    private static String getTccExecution(String xid, String bizKey) {
        return xid + "::" + bizKey;
    }

try

在try阶段方法最开始的时候,执行tagTryStart方法,标识try阶段开始执行,通过map实现记录整个TCC执行的过程状态。

为了解决空回滚,可以在自己的库里再加一张表,tcc Class名称,xid,业务id,当发生空回滚的时候,就往这张表里插入一条记录。

解决空悬挂的思路:即当rollback接口出现空回滚时,需要打一个标识(在数据库中查一条记录),在try这里判断一下

封装一个isEmptyRollback方法,如果查询到有空回滚的记录,就return true。

  if (isEmptyRollback()) {
    // 移除TCC标记
    TccResultHolder.removeResult(getClass(), skuCode, xid);
    return false;
}

在try方法结束的时候,标记一下try成功

  // 标识try阶段执行成功
TccResultHolder.tagTrySuccess(getClass(), skuCode, xid);

commit

在commit方法中,加入如下代码,判断holder中是否记录try成功记录,如果没有就return

  // 当出现网络异常或者TC Server异常时,会出现重复调用commit阶段的情况,所以需要进行幂等操作
if (!TccResultHolder.isTrySuccess(getClass(), skuCode, xid)) {
    return;
}

然后在commit方法结束,调用remove方法,移除TCC标记

  // 移除标识
TccResultHolder.removeResult(getClass(), skuCode, xid);

rollback

在rollback方法中,判断holder中是否有try记录,如果没有的话,说明此时出现了空回滚,往上面提到的空回滚表中加入一条记录,然后再return,不需要rollback了。

  // 空回滚处理
if (TccResultHolder.isTagNull(getClass(), skuCode, xid)) {
    log.info("mysql:出现空回滚");
    insertEmptyRollbackTag();
    return;
}
  // try阶段没有完成的情况下,不必执行回滚,因为try阶段有本地事务,事务失败时已经进行了回滚
// 如果try阶段成功,而其他全局事务参与者失败,这里会执行回滚
if (!TccResultHolder.isTrySuccess(getClass(), skuCode, xid)) {
    // 移除标识
    TccResultHolder.removeResult(getClass(), skuCode, xid);
    log.info("mysql:无需回滚");
    insertEmptyRollbackTag();
    return;
}

然后在rollback方法结束,调用remove方法,移除TCC标记

  // 移除标识
TccResultHolder.removeResult(getClass(), skuCode, xid);

Seata使用踩坑注意事项

假设一个请求基于 seata 对一条数据加了全局锁,并进行了本地分支事务提交。此时全局事务没有提交,另一个请求过来(这个请求就是简单的更新这条数据,不是分布式事务)自然就不需要获取全局锁,也就是可以直接更新,那全局事务再进行回滚时就有问题了,这也正是我们开发时需要考虑的地方,当有可能操作同一条数据引起并发问题的时候,给他加上全局事务,去获取全局锁。

还有听说有公司之前用Seata这个框架,生产上出大事故,导致账单数据经常丢失,而且经常出现死锁。负责开发的兄弟加班了几个月,每天晚上加班修复数据,后面弃用了seata,就没出现过这种事故了。因为公司经历过的这个事,导致那哥们儿对 Seata心存疑虑。

Dubbo+ZK整合Seata处理分布式事务

目前现有的落地方案都是spring cloud alibaba+nacos整合的seata,由于公司的技术栈为dubbo+zookeeper,并且seata官网并未给出zk整合seata的具体实现,所以我这里给出下面的方案重新整合。

image-20220424114734488.png

1. 本地安装seata server

下载 https://github.com/seata/seata/releases/download/v1.3.0/seata-server-1.3.0.zip

这里跟项目中的seata客户端依赖版本保持一致(Seata 1.4.2版本中数据库中时间字段不能使用datetime类型否则会引起序列化错误)

解压安装包得到如下目录

  .
├──bin
├──conf
└──lib

修改启动脚本⽂件,调整jvm内存。(机器内存⾜够的也可以不⽤改)

编辑seata-server.sh 或 seata-server.bat

修改120⾏处内容

默认堆内存是2048M,建议修改为512M即可,栈内存,永久代的值都可以不⽤改,原配置截图如下:

WX20220424-135437@2x.png 建议修改的值为

  -Xmx512m -Xms512m -Xmn256m

2. seata与zk整合,以zk为注册中心

这里以zk为例子,nacos网上资源很多,不做过多描述

修改conf文件夹里file.conf和registry.conf

registry.conf配置

  registry {
  type = "zk"
​
  zk {
    cluster = "default"
    serverAddr = "127.0.0.1:2181"
    sessionTimeout = 6000
    connectTimeout = 2000
    username = ""
    password = ""
  }
}
​
config {
  Type = "zk"
​
  zk {
    serverAddr = "127.0.0.1:2181"
    sessionTimeout = 6000
    connectTimeout = 2000
    username = ""
    password = ""
  }
  file {
    name = "file.conf"
  }
}

file.conf配置

  ## transaction log store, only used in seata-server
store {
  mode = "db"
​
  ## file store property
  file {
    dir = "sessionStore"
    # branch session size , if exceeded first try compress lockkey, still exceeded throws exceptions
    maxBranchSessionSize = 16384
    # globe session size , if exceeded throws exceptions
    maxGlobalSessionSize = 512
    # file buffer size , if exceeded allocate new buffer
    fileWriteBufferCacheSize = 16384
    # when recover batch read size
    sessionReloadReadSize = 100
    # async, sync
    flushDiskMode = async
  }
​
  ## database store property
  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"
    url = "jdbc:mysql://127.0.0.1:3306/seata"
    user = "root"
    password = "root"
    minConn = 5
    maxConn = 30
    globalTable = "global_table"
    branchTable = "branch_table"
    lockTable = "lock_table"
    queryLimit = 100
    maxWait = 5000
  }
​
}

修改 conf/nacos-config.txt配置为zk-config.properties

  service.vgroupMapping.order-service-seata-service-group=default
service.vgroupMapping.account-service-seata-service-group=default
service.vgroupMapping.storage-service-seata-service-group=default
service.vgroupMapping.business-service-seata-service-group=default
store.mode=db
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
store.db.user=root
store.db.password=root
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

3. 执⾏启动脚本(默认是8091端⼝)

  sudo chmod -R 777 seata/* 
# 执⾏启动脚本⽂件 
sudo bin/seata-server.sh
# 如果启动不了,提示Operation not permitted
sudo xattr -r -d com.apple.quarantine seata/*
# 执⾏启动脚本⽂件 
sudo bin/seata-server.sh

zk面板ls.png

zk面板seata.png

4. Dubbo项目pom.xml配置

${seata.version}版本要与seata server版本一致

  <!-- https://mvnrepository.com/artifact/io.seata/seata-all -->
<dependency>
    <groupId>io.seata</groupId>
    <artifactId>seata-spring-boot-starter</artifactId>
    <version>${seata.version}</version>
</dependency>

5. application.yml配置

  #seata配置 zk版
seata: 
  enable-auto-data-source-proxy: false # 如果引⼊的是seata-spring-boot-starter 请关闭⾃动代理 
  application-id: seata-server # Seata应⽤的名称 
  tx-service-group: default_tx_group # 事务组 
  service: 
    vgroup-mapping: 
      default_tx_group: default
  registry:
    type: zk
    zk:
      server-addr: 127.0.0.1:2181
      connect-timeout: 2000
      session-timeout: 6000
      cluster: default
  config:
    type: zk
    zk:
      server-addr: 127.0.0.1:2181
      connect-timeout: 2000
      session-timeout: 6000
  #seata配置 nacos版
seata: 
  enable-auto-data-source-proxy: false # 如果引⼊的是seata-spring-boot-starter 请关闭⾃动代理 
  application-id: seata-server # Seata应⽤的名称 
  tx-service-group: default_tx_group # 事务组 
  service: 
    vgroup-mapping: 
      default_tx_group: default
  config: 
    type: nacos 
    nacos: 
      server-addr: 127.0.0.1:8848 # Nacos服务地址 
      namespace: seata # Seata的NameSpace ID 
      group: SEATA_GROUP 
      username: nacos 
      password: nacos 
  registry: 
    type: nacos # 基于Nacos实现分布式事务管理 
    nacos: 
      server-addr: 127.0.0.1:8848 # Nacos服务地址 
      namespace: seata # Seata的NameSpace ID 
      group: SEATA_GROUP 
      username: nacos
      password: nacos 
      cluster: default
      application: seata-server

总结

自动挡跑车,好用但少用,容易出事情,抛砖引玉,欢迎大家讨论

上面提到的悬挂空回滚等问题解决方案,是一套可以落地的通用解决方案,如果大家有想法改进的可以提

不要怼我,我很脆弱

相关 [seata 分布] 推荐:

Seata-AT 如何保证分布式事务一致性

- - 掘金 后端
作者 | 陈健斌(funkye) github id: a364176773 来源| 阿里巴巴云原生公众号. Seata 是一款开源的分布式事务解决方案,star 高达 18100+,社区活跃度极高,致力于在微服务架构下提供高性能和简单易用的分布式事务服务,本文将剖析 Seata-AT 的实现原理,让用户对 AT 模式有更深入的认识.

分布式事务框架seata落地实践

- - 有道技术沙龙博客
seata是阿里巴巴研发的一套开源分布式事务框架,提供了AT、TCC、SAGA 和 XA 几种事务模式. 本文以精品课项目组的物流后台服务为例,介绍seata框架落地的过程,遇到的问题以及解决方案. 有道精品课教务系统是基于springcloud的分布式集群服务. 在实际业务中,存在许多分布式事务场景.

Seata分布式事务解决方案详解

- - 掘金 架构
什么是分布式事务,这里就不做解释了,介绍一下下面的常用分布式事务解决方案. Seata分布式事务框架:阿里巴巴2019年开源的分布式事务解决方案. 本文会详细分析AT和TCC原理以及对比,SAGA和XA暂时不在本文讨论中,后续会补上. 提一嘴,Saga不存在并发执行问题,因为Saga本质上是一个责任链模式,在同一个线程上有严格的先后执行驱动顺序.

ShardingSphere x Seata,一致性更强的分布式数据库中间件

- - IT瘾-dev
日前,分布式数据库中间件 ShardingSphere 将Seata 分布式事务能力进行整合,旨在打造一致性更强的分布式数据库中间件. 数据库领域,分布式事务的实现主要包含:两阶段的 XA 和 BASE 柔性事务. XA 事务底层,依赖于具体的数据库厂商对 XA 两阶段提交协议的支持. 通常,XA 协议通过在 Prepare 和 Commit 阶段进行 2PL(2 阶段锁),保证了分布式事务的 ACID,适用于短事务及非云化环境(云化环境下一次 IO 操作大概需要 20ms,两阶段锁会锁住资源长达 40ms,因此热点行上的事务的 TPS 会降到 25/s 左右,非云化环境通常一次 IO 只需几毫秒,因此锁热点数据的时间相对较低).

Spring Cloud Alibaba | 微服务分布式事务之Seata - 极客挖掘机 - 博客园

- -
Spring Cloud Alibaba | 微服务分布式事务之Seata. 本篇实战所使用Spring有关版本:. 在构建微服务的过程中,不管是使用什么框架、组件来构建,都绕不开一个问题,跨服务的业务操作如何保持数据一致性. 首先,设想一个传统的单体应用,无论多少内部调用,最后终归是在同一个数据库上进行操作来完成一向业务操作,如图:.

Seata 在蚂蚁国际银行业务的落地实践

- - 掘金 架构
文|李乔(花名:南桥)、李宗杰(花名:白鹰). 李乔:蚂蚁集团高级开发工程师,负责蚂蚁境外银行支付结算系统开发. 李宗杰:蚂蚁集团技术专家,负责蚂蚁分布式事务中间件研发. 本文 11580 字 阅读 25 分钟. 蚂蚁国际境外银行业务正在部分迁移至阿里云,原内部使用的 SOFA 技术栈无法在阿里云上得到支持.

分布式日志

- - Java - 编程语言 - ITeye博客
最近完成一个简单的日志管理系统,拿出来跟大家分享一下. 3、支持文件输出、habse输出、mongodb输出. 基于以上三点功能,我们下面详细说明. 说道支持这个功能,有个同事认为没有这个必要,他的观点是log4j的配置不需要经常变动,不需要支持这样的功能;本人的观点是“配置可以进行统一管理、而且正式机跟测试机的log4j的配置肯定会有一些差异的”,因此这个功能是必须的.

分布式事务简述

- If you are thinking one year ahead, you plant rice. If you are thinking twenty years ahead, you plant trees. If you are thinking a hundred years ahead, you educate people. - BlogJava-首页技术区
  随着系统越来越大,不断的模块化和SOA化,你的系统可能被分散于不同的机器上,这时候,你原先的单机本地事务可能已经无法满足你的需求,你可能要跨系统跨资源的去使用事务.   具体就不多介绍了,相信大家都能明白ACID特性的基本含义. 而一个具体的事务需要涉及到的模型(无论哪种模型)一般由下面几部分组成:.

Hadoop与分布式计算

- 透明 - 丕子
写本文由leftnoteasy发布于http://leftnoteasy.cnblogs.com 本文可以被全部或者部分的使用,但请注明出处,如果有问题,可以联系wheeleast (at) gmail.com, 也可以加作者的新浪微博:http://weibo.com/leftnoteasy. 很久没有写写博客了,之前主要是换工作,耽误了很多的时间,让人也变得懒散,不想花大时间来写东西.

分布式缓存-Memcached

- - 人月神话的BLOG
分布式缓存出于如下考虑,首先是缓存本身的水平线性扩展问题,其次是缓存大并发下的本身的性能问题,再次避免缓存的单点故障问题(多副本和副本一致性). 分布式缓存的核心技术包括首先是内存本身的管理问题,包括了内存的分配,管理和回收机制. 其次是分布式管理和分布式算法,其次是缓存键值管理和路由. 原文: http://wenku.baidu.com/view/8686d46c7e21af45b307a8c3.html.