Skip to content

virgo 客户端使用手册

1 概述

1.1 分布式事务问题

一个完整的业务往往需要调用多个子业务或服务,随着业务的不断增多,涉及的服务及数据也越来越多,越来越复杂。传统的系统难以支撑,于是出现了应用和数据库等的分布式系统。

在传统系统中,数据的一致性有数据库本身的事务机制保证。但在分布式系统中,一个完整的业务往往涉及到多个事务资源,或者会跨多个进程进行远程调用,此时数据库本身的事务机制已无法保证数据的一致性,从而产生了分布式事务的问题。

1.2 分布式事务问题的典型场景

1.2.1 跨事务资源

当应用进行分库以后,一次完整的业务访问的数据可能位于多个数据库中,但传统的数据库事务只能保证自身的事务。此时多个数据库之间的事务无法保证一致。

1543046113166

1.2.2 跨服务

分布式系统的一次完整的业务调用可能会跨多个进程,多个进程内的本地事务无法保证一致。

1543046160144

1.3 Virgo简介

Virgo 是一款用于解决上述分布式环境下的数据一致性问题分布式事务中间件。

Virgo 的架构如下所示:

1543046618317

  • Virgo 服务端:即事务协调器。负责分布式事务的推进,管理事务生命周期。

  • Virgo 客户端:即事务发起者。通过事务协调器,开启、提交、回滚分布式事务。同时包含部分资源管理器组件,负责管理和控制资源,与Virgo服务器进行交互。

2 使用场景

Virgo 本身可以单独与应用配合使用,也可以与 Libra 配合使用。在使用 Libra 进行分库时,为 Libra 提供分布式事务支持。

2.1 单独使用

2.1.1 跨事务资源(多个数据库)

在应用分库之后,如果需要保证多个数据库之间的数据一致,则会遇到第1.2.2节所描述的分布式事务问题。在这种情况下,使用 Virgo 如下图所示。

1543196113049

2.1.2 跨服务

在分布式系统中,跨服务调用时,如果需要保证多个服务之间的数据一致,则会遇到第1.2.2节所描述的分布式事务问题。在这种情况下,使用 Virgo 如下图所示。

1543196207083

2.2 与 Libra 配合使用

当使用 Libra 作为分布式数据中间件时,Virgo 可以与之配合,将 Libra 视为资源管理器。

2.2.1 使用 Libra 分库

如果应用系统只使用 Libra 分库,业务不会跨服务调用,且不涉及到其它事务资源时。此时可以将全局事务交由 Libra 控制,简化为如下场景。

1543196228379

2.2.2 跨服务+Libra

如果应用系统即使用 Libra 分库,又会跨服务调用,或涉及到其它事务资源时。此时调用如下。

1543196266350

或如下场景:

1543196280982

3 基础概念

3.1 事务

事务是指作为单个逻辑工作单元执行的一系列操作。处于同一个事务中的所有操作要么全部执行成功,要么全部执行失败。

通常事务机制由要操作的事务资源的提供者进行支持,例如数据库或支持事务的消息队列等。

一个逻辑工作单元要成为事务,必须满足所谓的ACID(原子性、一致性、隔离性和持久性)属性。

3.2 分布式事务

分布式事务(全局事务)是指事务发起者、资源管理器、事务协调者及资源分别位于不同的分布式系统的不同节点之上的事务。

一个分布式事务中可能包含多个事务资源的操作,并保证多个事务资源操作之间满足事务的特性。

3.3 主事务

一个主事务代表一个全局事务,包含此全局事务相关的所有信息及状态。事务管理器会给每个主事务分配唯一标识(XID)。并通过XID 关联主事务与事务分支。

3.4 事务分支

一个全局事务包含一个或多个事务分支组成。每个事务分支代表在一个参与事务的资源上的一次原子操作。

单个事务分支只能操作单一事务资源,但在同一全局事务中,同一事务资源可能创建多个事务分支。事务分支必须保证幂等以及原子性。事务管理器会给每个事务分支分配唯一标识(BXID)。

3.5 事务管理器

事务管理器(TM,TransacionManager) 负责管理及协调所有的全局事务,应用通过TM进行事务边界的定义。Virgo 的TM 分为服务端与客户端两部分:

  • 服务端:TM服务端负责事务流程的协调及相关日志记录,故障恢复等。在事务提交/回滚时,其协调所有参与指定全局事务的所有资源管理器共同完成提交/回滚操作。

  • 客户端:应用使用 Virgo 客户端接口TransactionManager进行事务边界的控制。其内部与服务端进行通信,并对事务线程上下文进行控制。

3.6 资源管理器

资源管理器(RM,ResourceManager) 管理需要参与全局事务的事务资源,事务管理器通过RM进行事务资源的管理以及事务流程的协调。RM 必须能够辨识其所管理的所有资源,并且配合TM完成事务分支的提交或回滚操作。

所有由事务管理器管理的每个事务资源必须指定全局唯一的标识(ResourceName),以确保事务管理器及资源管理器在进行事务协调时能够找到对应的资源。

3.7 事务上下文

事务上下文存储当前全局事务的相关信息,在应用开启全局事务后,自动生成相应的事务上下文,并在事务结束后进行清理。同一个上下文里的所有操作,被认为属于同一个全局事务。

Virgo 的事务上下文为线程级别,即在事务开始后,所有的操作应该在事务开启所在的线程完成。在同一个进程内部,同一事务不建议有并发或跨线程操作。

如果在事务过程中,需要调用远程服务,需要将事务上下文进行传播,以便使远程服务也能够加入当前事务

4 快速入门

关于本节

本节内容为您介绍如何使用Virgo安装包快速部署并启动一个Virgo服务,并简单了解Virgo的使用和管理。详细的配置及使用方式见第5、6、7节。

4.1 安装准备

Virgo 是使用 JAVA 语言进行编写开发,使用前需要先安装 JAVA 运行环境(JRE),由于 Virgo 中使用了 JDK8 中的一些特性,所以要求必须在 JDK8 以上的版本上运行。

4.2 安装部署服务端

使用 Virgo 时必须部署相应版本的服务端,服务端部署请参考服务端部署手册

4.3 应用集成客户端及使用

本小节以基于SpringBootMaven的应用为例,使用YAML格式的配置文件,演示集成virgo客户端及使用示例。

4.3.1 引入virgo-client-all依赖

在应用的pom.xml文件中加入以下依赖:

xml

<dependency>
    <groupId>com.dcits.virgo</groupId>
    <artifactId>virgo-client-all</artifactId>
    <version>3.3.0</version>
</dependency>
        <!-- virgo客户端从3.2.0版本开始,需要引入galaxy-configuration配置 -->
<dependency>
<groupId>com.dcits.galaxy</groupId>
<artifactId>galaxy-configuration</artifactId>
<version>3.0.0</version>
</dependency>

4.3.2 初始化数据库

根据数据库类型在发布包的客户端 db 目录中找到对应类型的安装脚本,并在数据库中执行,执行成功后数据库中应该会新建以下表:

virgo_undo_log
virgo_lock

例如,对于 MySQL 来说需要执行建表脚本 db/VirgoSDT_Install_For_Mysql.sql

4.3.3 修改 Virgo 客户端配置

SpringBoot的 yaml 配置文件中加入如下配置。

yaml
virgo:
  auto: true                  # 开启自动配置
  host: 127.0.0.1             # 已部署好的 TM 地址
  port: 8888                  # 已部署好的 TM 端口
  spring-trx-manager: false  # 是否注册 Spring 事务管理器适配,默认:false
  transport:
    connectTimeout: 3000
    heartbeat: 60000
    idleTimeout: 180000
    ioThreads: 4
  security: #客户端应用凭证信息,若服务端开启客户端鉴权,则必须设置该项
    token-uri: http://192.168.233.20:8189/oms/uaa/oauth/token #客户端认证地址
    client-id: xxx #客户端标识
    client-secret: yyy #客户端密钥
  sdt:
    intercepters:
      dataSource: test        # 拦截Id是 dataSource的Bean,注册资源名为test

4.3.4 示例代码

进行以上两个步骤后,Virgo 配置已完成。如果使用默认模式的话,不需要进行其它的配置或代码修改。

使用 Spring 的事务管理时,会自动打开相应的 Virgo 全局事务,并且使用dataSource进行的数据库的操作都会关联到当前全局事务中。

典型的示例如下:

java
pubic

class MbAcctDao {

    @Resource
    private DataSource dataSource;

    @Transactional
    public void updateDatabase() throws SQLException {
        //使用 dataSource 获取连接进行数据库操作
    }
}

5 客户端配置

Virgo 客户端提供对于SpringSpring Boot的适配,并对于未使用Spring框架或需要自定义的应用提供了编程式的配置接口。

提示

对于使用了Spring框架的应用,建议使用Spring BootSpring方式的配置

5.1 Spring Boot 配置

基于Spring Bootyaml格式的配置示例如下

yaml
galaxy:
  dataCenter: xian                 # 开启双中心功能必须配置,表示应用所属中心
  appId: testApp                   # 表示应用ID,使用TCC时必须配置

virgo:
  enabled: true                       # virgo client 自动配置开关,默认为true。如果设为 false,则不会自动配置
  host: 127.0.0.1                  # virgo-tm 服务端地址,默认:127.0.0.1
  port: 8888                       # virgo-tm 服务端端口,默认:8888
  annotation: enable               # 是否开启 Virgo 事务注解处理,默认: enable
  loadBalance: round-robin         # 默认轮询策略,round-robin(轮询)/ random (随机)
  spring-trx-manager: false       # 是否注册 Spring 事务管理器适配,默认:false
  trx:
    defaultTrxTimeout: 90000       # 默认事务超时时长,单位毫秒,默认值:90000
  transport: # 配置与 virgo-tm 服务端的通信相关配置
    connectTimeout: 3000           # 建立连接的超时时间,单位毫秒,默认: 3000
    heartbeat: 60000               # 连接心跳时间,超过此时间未发送或接收数据时,发送心跳包,单位毫秒,默认: 60000
    idleTimeout: 180000            # 连接空闲超时时间,超过此时间未发送或接收数据时,断开连接,单位毫秒,默认: 180000
    defautlTimeout: -1             # 通信超时时间,单位毫秒,默认: -1
    reconnectPeriod: 2000          # 连接断开后重连周期,单位毫秒,默认: 2000
    maxReconnectPeriod: 2000       # 最大断开重连周期,单位毫秒,默认:2000
    ioThreads: 8                   # 通信IO处理线程数,默认:运行时可用处理器数量+1

  resource:
    threads: 8                     # 处理提交或回滚请求的最大线程数,默认: 运行时可用处理器数量+1
    keepAliveTime: 0               # 线程池保持活跃时间,单位秒,默认:0
    bufferQueues: -1               # 线程池任务队列大小

  tcc:
    enable: true                    # 是否开启 TCC 事务注解处理
    #cleanType: shard               # 防悬挂日志清理策略 shard:分库清理模式,default: 默认非分库模式
    #cleanMode: Minute              #防悬挂日志清理周期 Close:不清理 ,Day:按天清理,默认1天,Hour:按小时清理,默认1小时,Minute:按分钟清理,默认30分钟
    #cleanPeriod: 1                 #清理时间,与cleanMode 对应
  message:
    enable: true                    #是否开启可靠消息模式	
    type: mq                        #开启可靠消息自动化配置
  sdt:
    intercepters: # 哪些数据源应该被 virgo sdt 模式进行管理
      dataSource: test             # key 为对应的 DataSource 的 Bean Id, value 为此数据源对应的数据库的唯一标识
    lock:
      type: table                  # 逻辑锁类型,默认为 table, 可以配置为 xidcolumn, table。建议采用默认配置
      retry: false                 # 是否默认进行锁重试,默认为 false
      maxRetryTime: 0              # 锁重试超时时间,只有 retry 为 true 时才生效
      clean:
        rate: 0                    # 逻辑锁表清理频率,默认为 0,表示不开启锁清理。配置大于 0 时定时清理逻辑锁表。单位:毫秒
        batch: 1000                # 清理逻辑锁表时分段大小,默认 1000,建议使用默认值。
        maxOffset: 5000            # 每次最多清理逻辑锁表分段起始偏移量大小,默认 5000,建议使用默认值。
  xa:
    intercepters: # 哪些数据源应该被 virgo xa 模式进行管理
      dataSource: test             # key 为对应的 DataSource 的 Bean Id, value 为此数据源对应的数据库的唯一标识
  mdc:
    globalTraceKey: globalSeqNo    # 交易日志跟踪:全局交易流水号名称,具体由外部系统情况决定
    subTraceKey: subSeqNo          # 交易日志跟踪:子交易流水号名称,具体由外部系统情况决定

5.2 Spring 配置

如果在 Spring 中使用 Virgo,或 Spring Boot的默认配置不满足需求,可以直接配置对应的Bean。如果是在 Spring Boot 中使用此方式配置,会覆盖VirgoAutoConfigreation注册的对应Bean

以下以 xml 配置为示例

5.2.1 Virgo 事务管理器

事务管理器工厂(TransactionManagerFactory)实现FactoryBean,生成应用使用的事务管理器实例,此Bean中的属性与Spring Boot 配置方式中的virgo.client的配置一一对应,不重复介绍。

xml

<bean name="globalTransactionManager" class="com.dcits.virgo.spring.config.TransactionManagerFactory">
    <property name="host" value="127.0.0.1"/>
    ...
</bean>

5.2.2 数据源包装

在 SDT 事务模式下,应用只有使用 Virgo 提供的数据源才能被 Virgo 全局事务所管理。Virgo 两种将原有数据源包装为VirgoDataSource的方法,任选其一即可。

提示

注意: 应用必须确保在执行过程中使用包装过的VirgoDataSource来进行数据操作。

假设原本有以下数据源定义:

xml

<bean name="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="url" value="${jdbc.url}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
</bean>
5.2.2.1 SDT 数据源自动包装

VirgoSdtDataSourceProcessor提供自动包装指定数据源的方式,其属性与Spring Boot配置方式的virgo.intercepters 含义相同。在这种方式下,应用中直接注入或获取原本的DataSource使用即可。

xml

<bean id="virgoDataSourceProcessor" class="com.dcits.virgo.spring.config.datasource.VirgoSdtDataSourceProcessor">
    <!-- 哪些数据源应该被 Virgo 进行管理,对于不在下列表中的数据源操作,不会加入 Virgo 全局事务 -->
    <!-- key 为对应的 DataSource 的 Bean Id, value 为此数据源对应的数据库的唯一标识 -->
    <property name="intercepters">
        <map>
            <entry key="dataSource" value="test"/>
        </map>
    </property>
</bean>
5.2.2.2 XA 数据源自动包装

VirgoXaDataSourceProcessor提供自动包装指定数据源的方式,其属性与Spring Boot配置方式的virgo.xaintercepters 含义相同。在这种方式下,应用中直接注入或获取原本的DataSource使用即可。

xml

<bean id="virgoDataSourceProcessor" class="com.dcits.virgo.spring.config.datasource.VirgoXaDataSourceProcessor">
    <!-- 哪些数据源应该被 Virgo 进行管理,对于不在下列表中的数据源操作,不会加入 Virgo 全局事务 -->
    <!-- key 为对应的 DataSource 的 Bean Id, value 为此数据源对应的数据库的唯一标识 -->
    <property name="intercepters">
        <map>
            <entry key="dataSource" value="test"/>
        </map>
    </property>
</bean>
5.2.2.3 手动配置 Virgo 数据源

如果应用中一个数据源只有部分场景需要加入Virgo事务或其它需要自定的情况,可以对需要处理的bean 进行手动配置。以下为例,需要加入Virgo事务的使用virgoDataSource否则使用dataSource

xml
<!-- 配置Virgo数据源:resourceName值为数据源ID(ID全局唯一),target原生数据源实例bean -->
<bean id="virgoDataSource" class="com.dcits.virgo.rm.sdt.jdbc.wrapper.SmartTrxDataSource">
    <!-- 每个数据源必须分配一个全局唯一的 ResourceName -->
    <property name="resourceName" value="id"/>
    <!-- 需要包装的目标数据源 -->
    <property name="target" ref="dataSource"/>
</bean>
5.2.2.4 SDT 逻辑锁表自动清理

VirgoTableLockCleaner提供SDT 逻辑锁表自动清理功能,其属性与Spring Boot配置方式的virgo.sdt.lock.clean前缀的配置项含义相同。

xml

<bean id="virgoTableLockCleaner" class="com.dcits.virgo..sdt.parser.lock.VirgoTableLockCleaner"
      init-method="enableClean" destroy-method="disableClean">
    <property name="rate" value="0"/>
    <property name="batch" value="1000"/>
    <property name="maxOffset" value="5000"/>
</bean>

5.2.4 Virgo 事务注解处理

如需使用@VirgoTransactional,需要配置其注解扫描器。此项对应SpringBoot配置中的virgo.annotation

xml
<!-- Virgo事务管理器配置 -->
<bean id="virgoAnnoScanner" class="com.dcits.virgo.spring.config.VirgoTransactionalScanner">
    <!-- 处理注解事务的事务管理器 -->
    <property name="transactionManager" ref="globalTransactionManager"/>
    <!-- 是否创建基于类的代理,默认为: false -->
    <property name="proxyTargetClass" value="true"/>
</bean>

5.2.5 TCC 事务注解处理

目前默认的 TCC 事务通过@TccBranch注解指定,如需使用 TCC 事务,需要开启@TccBranch注解扫描。此项对应SpringBoot 配置中的virgo.tcc

xml
<!-- Virgo事务管理器配置 -->
<bean id="tccAnnoScanner" class="com.dcits.virgo.spring.config.tcc.TccBranchScanner">
    <!-- 是否创建基于类的代理,默认为: false -->
    <property name="proxyTargetClass" value="true"/>
</bean>

5.2.6 Spring 事务管理器适配

Virgo 提供了 Spring 事务管理器的适配VirgoPlatformTransactionManager,在配置此项后,可以通过 Spring 原生事务 API 或注解同时控制本地事务与全局事务。此项对应SpringBoot配置中的virgo.spring-trx-manager

xml

<bean id="transactionManager" class="com.dcits.virgo.spring.transaction.VirgoPlatformTransactionManager">
    <!-- Virgo数据源,配置见[VirgoDataSource] -->
    <property name="dataSource" ref="virgoDataSource"/>
    <!-- Virgo 事务管理器 -->
    <property name="globalTransactionManager" ref="globalTransactionManager"/>
</bean>
5.2.6.1 配置开启 Spring 基于注解的事务管理
xml
<!-- 配置基于注解的事物管理 -->
<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>

5.3 使用 API 配置客户端

5.3.1 事务管理器

com.dcits.virgo.client.Bootstrap提供了初始化事务管理器及资源管理器的功能,用法如下。

java
Bootstrap bootstrap = new Bootstrap();
//所有属性对应 SpringBoot 适配的 virgo.client 或 TransactionManagerFactory。此处不详细解释。
bootstrap.

setHost("127.0.0.1");
bootstrap.

init();

提示

目前 Virgo 只提供基于 Spring 的注解处理,所以默认不支持在非 Spring 环境下使用 TCC 模式

5.3.1.1 获取事务管理器

在 API 配置方式下,有两种获取事务管理器实例的方式。

  1. 通过 Bootstrap
java
//通过之前初始化好的 Bootstrap 实例获取,推荐使用此方法获取 TransactionManager 实例后,由应用自动控制
TransactionManager tm = bootstrap.getTransactionManager();
  1. TransactionManagerImpl 静态方法
java
//在Bootstrap初始化完成后,可以通过此静态方法获取实例
TransactionManager tm = TransactionManagerImpl.getInstance();

5.3.2 数据源包装

使用 API 的方式配置客户端,则只能在数据源初始化之处通过相应的 API 进行包装,然后应用中使用包装过后的数据源。

java
DataSource target; // 已初始化好的原始数据源
String resourceName; // 每个数据源必须分配一个全局唯一的 ResourceName
SmartTrxDataSource trxDataSource = new SmartTrxDataSource(resourceName, target);

5.4 交易跟踪配置

Virgo客户端默认采用MDC方式获取业务的交易流水号。事务开启之前,业务系统必须配置对应的流水,结束后进行对应的清理工作。

Spring Boot配置:

yaml
virgo:
  mdc:
    globalTraceKey: globalSeqNo    # 交易日志跟踪:全局交易流水号名称,具体由外部系统情况决定
    subTraceKey: subSeqNo          # 交易日志跟踪:子交易流水号名称,具体由外部系统情况决定

6 应用使用方式

6.1 总览

Virgo分布式事务提供了 SDT,TCCXA 三种解决方案,它们均有各自的特点及适合场景。在业务开发使用方面支持独立使用,同时兼容 SpringSpringBoot 。本章将简单介绍两种事务模式以及现阶段提供的几种使用方式并给出相应使用代码示例。

6.1.1 SDT模式(默认模式)

SDT模拟数据库事务 )模式是一种业务无侵入的业务解决方案。它解决了分布式事务的易用性问题,在该模式下,开发者只需关注一阶段操作,框架会自动解析SQL 语义,生成二阶段提交和回滚操作,使分布式事务的接入更便捷,该模式下对业务代码几乎无侵入,框架能够“自动化”地解决分布式架构下的数据一致性问题。 该模式下Virgo客户端工作在JDCB APIJDBC SPI之间。在业务应用中发挥作用的位置如下图:

1542854428052

6.1.2 TCC模式

TCC模式的解决方案其实是两阶段提交的一种改进,将整个业务逻辑的每个分支分成了TryConfirmCancel 三个操作,其中Try部分完成业务的准备工作、Confirm部分完成业务的提交、Cancel部分完成事务的回滚。这三步仅仅是方法论,具体在每一步的操作实现,则由所涉及的服务自行设计代码实现。以简单的A向B转账为例,A加钱与B减钱的操作由两个参与方服务来实现,A和B的两个Try会同时进行业务系统检测和资源预留,只有两个Try都成功了才会往下进行Confirm操作以提交金额的增减。对于复杂的操作,还会在一个分布式事务里嵌套多层参与方,只有每层的Try都成功了,才会完成整个分布式事务的第一阶段,中间一旦任何一层失败都会回滚。 该模式的运作机理如下图:

1542857244859

6.1.3 XA模式

XA 模式即传统的两阶段提供协议,提供数据库层面的分布式事务原生支持。将事务提交分为preparecommit两个阶段来确保分布式事务的一致性。

6.1.4 可靠消息模式

可靠消息 模式,采用本地消息表方案,在业务执行完毕后先将要发送的消息存储在本地消息表中,消息存储与业务执行通过本地事务控制,保证一致性,在事务提交阶段再将消息发送到消息队列中供消费者消费,可靠消息保证在业务逻辑执行成功的情况下至少发送一次消息到队列中。

6.2 全局事务管理

Virgo提供一种非Spring框架下直接使用API管理事务的使用方式的同时,也对Spring/SpringBoot的主流开发框架做了必要的适配。

6.2.1 使用 Spring 注解

通过集成Spring的方式使用Virgo时,由于Virgo实现并增强了Spring的事务管理器,故业务编码中的使用方式与使用Spring事务完全一致。示例如下:

java
/**
 * 使用 Virgo 提供的 Spring 事务管理器实现同时管理全局事务与本地事务分支
 */
@Service
public class SpringTransactionSample {

    @Transactional  // 使用Spring的事务注解管理全局事务和本地事务
    public void doInTrx() {
        // do same busi work
        doSomeWork();
    }
}

警告

由于目前版本中Virgo仅支持分支事务(本地事务)嵌套,尚不支持全局事务的嵌套(一个全局事务中嵌套另一个全局事务),故对于NESTED 的Spring的事务传播行为暂不支持;

由于VirgoPlatformTransactionManager是对Spring事务管理器 DataSourceTransactionManager 的增强,也受Spring事务管理器的限制,目前只支持同时管理一个数据源,尚未提供多数据源的支持。

6.2.2 使用 @VirgoTransactional

在使用了Spring/SpingBoot框架,也可以使用@VirgoTransactional 注解管理分布式事务自动实现开启、提交、回滚分布式事务,使用Spring的@Transactional注解管理本地事务,示例如下:

java
/**
 * Virgo TransactionManager 管理全局事务,使用 Spring 原生的事务管理器管理全局事务中的本地事务分支
 */
@Service
public class ProgramingSample {
    @Resource
    private SimpleDao dao;

    // Virgo注解管理全局事务
    @VirgoTransactional
    public void doInTrx() {
        // do same busi work
        dao.doSomeWork(xxx, xxx);
    }
}

/**
 * 使用 Spring 原生的事务管理器管理全局事务中的本地事务
 */
@Component
public class SimpleDao {
    // Spring注解管理本地事务
    @Transactional
    public int doSomeWork(Object xxx, Object xxx) {
        // do same busi work
        ......
    }
}

提示

该场景下Spring事务资源管理器要配置为Spring原生的DataSourceTransactionManager 。此时,Spring事务管理器的本地事务与Virgo全局事务管理器各自管理本地事务和全局事务。

6.2.3 使用 TransactionManager 编程式管理

Virgo提供一种非Spring框架下直接使用API管理事务的使用方式。

事务开启、提交、回滚均由开发者自己控制,代码示例如下:

java
/**
 * Virgo TransactionManager 管理全局事务
 */
public class ProgramingSample {
    private TransactionManager tm = TransactionManagerImpl.getInstance();

    public void doInTrx() {
        tm.begin(90000);            // 开启全局事务
        try {
            // do same busi work
            ......                      // 正常实现业务逻辑
            tm.commit();        // 提交全局事务
        } catch (Exception e) {
            tm.rollback();      // 异常处理中回滚全局事务
        }
    }
}

6.2.4 事务上下文传播

Virgo 通过将事务信息绑定到线程上下文以实现全局事务的传递,当需要跨应用协同完成同一个全局事务时,可以通过以下方式实现事务信息的传播。Virgo 默认提供常见几种的 rpc 及微服务框架的下自动传播全局事务的模块,相关坐标如下:

xml
<!-- dubbo 事务传播及导入支持 -->
<dependency>
    <groupId>com.dcits.virgo</groupId>
    <artifactId>virgo-rpc-dubbo</artifactId>
</dependency>

        <!-- feign 接出时,传播全局事务支持,可配合:
                virgo-rpc-servlet-filter、
                virgo-rpc-webflux-filter或
                virgo-rpc-gravity 使用
         -->
<dependency>
<groupId>com.dcits.virgo</groupId>
<artifactId>virgo-rpc-feign</artifactId>
</dependency>

        <!-- servlet 服务端导入传递的全局事务 -->
<dependency>
<groupId>com.dcits.virgo</groupId>
<artifactId>virgo-rpc-servlet-filter</artifactId>
</dependency>

        <!-- webflux 服务端导入传递的全局事务 -->
<dependency>
<groupId>com.dcits.virgo</groupId>
<artifactId>virgo-rpc-webflux-filter</artifactId>
</dependency>

        <!-- gravity 服务导入传递的全局事务 -->
<dependency>
<groupId>com.dcits.virgo</groupId>
<artifactId>virgo-rpc-gravity</artifactId>
</dependency>
6.2.4.1自定义事务上下文传播

如果使用不在以上支持范围的 rpc 或微服务框架,可以自行处理对应的事务上下文传播。以Dubbo为例示例如下:

java

@Activate(group = {Constants.CONSUMER, Constants.PROVIDER})
public class VirgoTrxFilter implements Filter {

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        // 忽略 inJvm 协议
        if (Constants.LOCAL_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
            return invoker.invoke(invocation);
        }

        String side = invoker.getUrl().getParameter(Constants.SIDE_KEY);
        if (Constants.CONSUMER_SIDE.equals(side)) {
            handleConsumer((RpcInvocation) invocation);
        } else if (Constants.PROVIDER_SIDE.equals(side)) {
            return handleProvider(invoker, invocation);
        }
        return invoker.invoke(invocation);
    }

    /**
     * RPC 调用方将尝试附带XID信息以便传播该全局事务
     * @param invocation
     */
    private void handleConsumer(RpcInvocation invocation) {
        if (!TransactionContext.inGlobalTrx()) {
            return;
        }
        invocation.setAttachment(com.dcits.virgo.common.Constants.X_VIRGO_KEY, TransactionContext.exportTransaction());
    }

    /**
     * RPC 服务方将尝试获取XID信息以便传播该全局事务
     * @param invocation
     */
    private Result handleProvider(Invoker<?> invoker, Invocation invocation) {
        String xid = invocation.getAttachment(com.dcits.virgo.common.Constants.X_VIRGO_KEY);
        if (xid == null || xid.isEmpty()) {
            return invoker.invoke(invocation);
        }
        TransactionContext.importTransaction(xid);
        try {
            return invoker.invoke(invocation);
        } finally {
            TransactionContext.unbind();
        }
    }

}

6.2.5 相关 API 说明

6.2.5.1 全局事务相关API

全局事务相关API位于接口com.dcits.virgo.client.TransactionManager

  • 获取单例的全局事务操作对象

    TransactionManager trxManager = TransactionManagerImpl.getInstance();

    其中,TransactionManagerImpl为单例对象

  • 开启分布式事务

    1. trxManager.begin(long timeout)
      • timeout 为设置事务超时时间,单位毫秒,开启的事务上下文会绑定到当前线程中;
    2. trxManager.begin(long timeout, Scope scope)
      • timeout 为设置事务超时时间,单位毫秒;
      • scope 为事务作用范围;
  • 提交分布式事务

    1. trxManager.commit()
      • 根据当前线程中的事务上下文提交分布式事务。
  • 回滚分布式事务

    1. trxManager.rollback()
      • 根据当前线程中的事务上下文回滚分布式事务。
  • 查询分布式事务状态

    TrunkState state = trxManager.queryGlobalStatus(String xid, String dataCenter)

    • xid 全局事务ID;
    • dataCenter XID所在的数据中心ID,双中心模式下不可为空;
    • state 返回全局事务的状态;
6.2.5.2 线程上下文相关API

Virgo事务是与当前线程绑定的,事务相关信息存于当前线程的线程上下文中。

  1. TransactionContext context = TransactionContext.getContext()
    • 获取当前线程上下文包含的分布式事务信息
    • context 为分布式事务信息载体
  2. TransactionContext.bind(String xid)
    • 分布式事务ID关联到当前线程上下文中
    • xid 为传入要关联的分布式事务ID
  3. TransactionContext.unbind()
    • 分布式事务ID与当前线程上下文接触关联
  4. boolean flag = TransactionContext.inTrx()
    • 查看当前线程上下文是否处于分布式事务中
  5. TransactionContext context = TransactionContext.suspend()
    • 当前线程上下文中挂起分布式事务
    • context 为挂起的分布式事务信息载体
  6. TransactionContext.resume(TransactionContext resume)
    • 当前线程上下文中恢复分布式事务
    • resume 为传入要恢复的分布式事务信息载体
6.2.5.3 事务信息相关API

Virgo用户可见的事务信息包括全局事务ID(XID)分支事务ID(BXID)分支事务模式(TrxMode)

以下操作是基于获取事务信息载体类TransactionContext的操作:

  • TransactionContext context = TransactionContext.getContext()
  1. String xid = context.getXid()
    • 获取分布式事务XID
  2. String bxid = context.getBxid()
    • 获取分布式事务BXID

6.3 默认模式(SDT)

6.3.1 全局事务与本地事务、分支事务的关系

在Virgo中,本地事务的概念等价于Virgo分布式事务中的分支事务,以下称为分支事务。

分支事务是以Connectioncommit执行为节点:

  • 当创建的Connection使用默认的autoCommit=true方式,则自动commit。即对于每一次的insert/delete/update 回紧接着进行提交,每一调语句都会注册一个新的分支事务;
  • 当创建的Connection并设置autoCommit=false,则需要手动commit。即可以操作多个insert/delete/update 后统一手动commit,而此时只在commit时注册一个分支事务。

全局事务通过统一协调一组分支事务来保证分支事务的统一的提交与回滚。

6.3.2 Spring 管理全局事务与本地事务

第7.2.1节所属,可以通过Spring@Transactional注解同时管理全局事务与本地事务,并兼容除NESTED 外的6中事务传播行为。具体的行为影响如下所述。

  1. PROPAGATION_REQUIRED(最常用)

    原生Spring事务行为:支持当前本地事务,假设当前没有本地事务,就新建一个本地事务。

    兼容Virgo事务后的行为:

    • 如当前没有本地事务也没有全局事务,则先开启一个全局事务并新建一个本地事务;
    • 如当前没有本地事务但已开启全局事务,则新建一个本地事务并加入该全局事务中;
    • 如当前已有本地事务也有全局事务,则支持当前本地事务并加入该全局事务中;
    • 不存在当前开启本地事务但没有开启全局事务的情况。
  2. PROPAGATION_SUPPORTS

    原生Spring事务行为:支持当前事务,假设当前没有事务,就以非事务方式运行。

    兼容Virgo事务后的行为:

    • 如当前没有本地事务也没有全局事务,则以非事务方式运行;
    • 如当前没有本地事务但已开启全局事务,则不开启本地事务但将其加入全局事务中;
    • 如当前已有本地事务也有全局事务,则支持当前本地事务并加入该全局事务中;
    • 不存在当前开启本地事务但没有开启全局事务的情况。
  3. PROPAGATION_MANDATORY

    原生Spring事务行为:当前必须有本地事务,否则抛出异常。

    兼容Virgo事务后的行为:

    • 如当前没有本地事务也没有全局事务,则抛出异常;
    • 如当前没有本地事务但已开启全局事务,则抛出异常;
    • 如当前已有本地事务也有全局事务,则支持当前本地事务并加入该全局事务中;
    • 不存在当前开启本地事务但没有开启全局事务的情况。
  4. PROPAGATION_REQUIRES_NEW

    原生Spring事务行为:新建事务,假设当前存在本地事务,则把当前本地事务挂起,重新开启一个本地事务。

    兼容Virgo事务后的行为:

    • 如当前没有本地事务也没有全局事务,开启一个全局事务并新建一个本地事务;
    • 如当前没有本地事务但已开启全局事务,新建一个本地事务并加入到该全局事务中;
    • 如当前已有本地事务也有全局事务,挂起该全局事务和本地事务,新开启一个全局事务并新建一个本地事务;
    • 不存在当前开启本地事务但没有开启全局事务的情况。
  5. PROPAGATION_NOT_SUPPORTED

    原生Spring事务行为:以非本地事务方式运行。假设当前存在本地事务,就把当前本地事务挂起 。

    兼容Virgo事务后的行为:

    • 如当前没有本地事务也没有全局事务,以非事务方式运行;
    • 如当前没有本地事务但已开启全局事务,将全局事务挂起,本地以非事务方式运行;
    • 如当前已有本地事务也有全局事务,将全局事务与本地事务挂起,以非事务方式运行;
    • 不存在当前开启本地事务但没有开启全局事务的情况。
  6. PROPAGATION_NEVER

    原生Spring事务行为:以非事务方式运行,假设当前存在事务,则抛出异常。

    兼容Virgo事务后的行为:

    • 如当前没有本地事务也没有全局事务,以非事务方式运行;
    • 如当前没有本地事务但已开启全局事务,抛出异常;
    • 如当前已有本地事务也有全局事务,抛出异常;
    • 不存在当前开启本地事务但没有开启全局事务的情况。
  7. PROPAGATION_NESTED

    尚不支持,直接抛出异常NesetdTransactionNotSupportedException

6.3.3 Spring 管理本地事务

Virgo支持原生Spring事务管理器Virgo全局事务管理器同时使用,即通过Virgo的@VirgoTransactional 注解管理全局事务和Spring的@Transactional注解管理本地事务,使用示例参见第7.2.2节

6.3.4 编程式开发

Virgo提供一种非Spring框架下直接使用API管理事务的使用方式。

开发者可以直接使用TransactionManager进行编程式开发,参见第7.2.3节

6.3.5 与 Libra 配合使用

6.3.5.1 Libra 管理全局事务

当在第2.2.1节所示场景下,即应用只使用 Libra 分库,且不涉及其它事务性资源以及服务调用时,可以将全局事务控制交由 Libra 控制,应用不用集成 Virgo 客户端,直接使用 Libra 的本地事务即可。

Libra 默认不会主动开启分布式事务,需要在开启本地事务后,通过set virgo_xa=1告知Libra开启全局事务。示例如下:

java
try(Connection conn = dataSource.getConnection()){
        conn.

setAutoCommit(false);
    conn.

createStatement().

execute("set virgo_xa=1");
//执行业务操作
    conn.

commit();
}
6.3.5.2 应用管理全局事务

当在第2.2.2节所示场景下,一次业务调用不但使用Libra分库,还存在远程服务调用或其它事务资源(如消息队列) ,此时由应用管理全局事务,并通过set virgo_xid='当前全局事务的XID'告知 Libra 加入到本次全局事务。

如果系统已集成 Virgo 客户端,只要按照第6节的配置,将对应 Libra 的数据源进行包装即可,不需要额外操作。

6.3.6 方法级别锁重试配置

通过virgo.sdt.lock 属性可以配置全局默认的锁重试策略及超时时间,但某些交易可能需要不同于全局的锁重试策略或超时时间,sdt 也提供在某个事务中或某个方法中单独指定锁策略的方式。

提示

不论通过以下哪种方式指定的锁策略,都无法远程传播到其他微服务中

6.3.6.1 使用 @SdtProperties

在需要单独设置锁策略的方法上增加@SdtProperties注解,在注解属性中指定锁信息。

java

@Transactional
// retryLock: 是否开启锁重试,默认为 false。如果为 false, 采用全局配置
// lockRetryTimeout: 锁重试超时时间,默认为 -1。如果不大于 0, 采用全局配置
@SdtProperties(retryLock = true, lockRetryTimeout = 3000)
public void doInTrx() {
    // do same busi work
    doSomeWork();
}
6.3.6.2 使用 SdtBranchContext

通过SdtBranchContext 执行锁策略,相比@SdtProperties更灵活一些,如果不主动清除SdtBranchContext ,将会一直持续生效到当前全局事务结束(不能远程传播到其他微服务)。与使用@SdtProperties等效的例子如下:

java

@Transactional
public void doInTrx() {
    try (SdtBranchContext context = SdtBranchContext.newSdtBranch()) {
        context.retryLock(true); // retryLock: 是否开启锁重试,默认为 false。如果为 false, 采用全局配置
        context.lockRetryTimeout(3000); // lockRetryTimeout: 锁重试超时时间,默认为 -1。如果不大于 0, 采用全局配置
        // do same busi work
        doSomeWork();
    }
}

6.4 TCC 模式

TCC事务提供了@TccBranch事务注解的使用方式,其注解扫描借助Spring的注解扫描实现。

基于Spring/SpingBoot并在开启Spring的注解扫描的情况下,可以使用TCC注解管理了TCC事务,代码示例如下:

java

@Service
public class TccService {
    // TransactionManager为第6节中配置好的Virgo事务管理器
    @Resource(name = "transactionManagerFactory")
    private TransactionManager tm;

    /**
     * 资源预留业务逻辑,通过@TccBranch注解指定Try/Confirm/Cancel的方法名
     * @name 指定方法ID,全局唯一
     * @confirmMethod 指定同一个类中确认业务的方法名
     * @cancelMethod 指定同一个类中取消业务的方法名
     */
    @TccBranch(name = "tcc_try_busi_01", confirmMethod = "tccConfirmBusi", cancelMethod = "tccCancelBusi")
    public void tccTryBusi(TccAcct tccAcct) {
        try {
            // do same busi work
            ...... // 实现资源预留的业务逻辑
        } catch (Exception e) {
            // exception filter
            if (needRollback()) {
                tm.rollback();
            }
        }
    }

    /**
     * 此处编写确认业务逻辑,发起分布式事务提交时Virgo会自动调用该方法
     * 通过全局事务ID、分支事务ID供业务关联try阶段的资源预留方法
     * @xid 全局事务ID
     * @bxid 分支事务ID
     */
    public void tccConfirmBusi(String xid, String bxid) {
        // do confirm work
        ...... // 实现确认的业务逻辑
    }

    /**
     * 此处编写取消业务逻辑,发起分布式事务回滚时Virgo会自动调用该方法
     * 通过全局事务ID、分支事务ID供业务关联try阶段的资源预留方法
     * @xid 全局事务ID
     * @bxid 分支事务ID
     */
    public void tccCancelBusi(String xid, String bxid) {
        // do cancel work
        ...... // 实现取消的业务逻辑
    }
}

6.4.1 TCC防悬挂与幂等机制

TCC防悬挂的主要目的是防止空回滚,即二阶段的cancel执行时间早于一阶段try方法,导致try预留的资源不能释放。TCC幂等控制的目的是保证同一个事务内,一阶段try方法只会被执行一次,以及二阶段的commit方法只会被执行一次,重复提交会被幂等拦截。

防悬挂涉及到日志记录,分为分库模式与非分库模式,在分库模式下需要满足一下要求

  • 应用中使用的分库规则必须一致
  • 在实现一阶段方法try前需要知道分片键值,即作为try方法参数上送
java

@Service
public class TccService {
    // TransactionManager为第6节中配置好的Virgo事务管理器
    @Resource(name = "transactionManagerFactory")
    private TransactionManager tm;

    /**
     * 资源预留业务逻辑,通过@TccBranch注解指定Try/Confirm/Cancel的方法名
     * @name 指定方法ID,全局唯一
     * @confirmMethod 指定同一个类中确认业务的方法名
     * @cancelMethod 指定同一个类中取消业务的方法名
     * @idempotent = true,是否开启防悬挂功能
     * @isSharding = true,在开启防悬挂前提下是否分库
     */
    @TccBranch(name = "tcc_try_busi_01", confirmMethod = "tccConfirmBusi", cancelMethod = "tccCancelBusi"idempotent= true, isSharding = true)
    //在分库模式下,try方法必须通过@ShardingKey注解上送分片值
    public void tccTryBusi(TccAcct tccAcct, @ShardingKey String shardingKey) {
        try {
            // do same busi work
            ...... // 实现资源预留的业务逻辑
        } catch (Exception e) {
            // exception filter
            if (needRollback()) {
                tm.rollback();
            }
        }
    }

    /**
     * 此处编写确认业务逻辑,发起分布式事务提交时Virgo会自动调用该方法
     * 通过全局事务ID、分支事务ID供业务关联try阶段的资源预留方法
     * @xid 全局事务ID
     * @bxid 分支事务ID
     */
    public void tccConfirmBusi(String xid, String bxid) {
        // do confirm work
        ...... // 实现确认的业务逻辑
    }

    /**
     * 此处编写取消业务逻辑,发起分布式事务回滚时Virgo会自动调用该方法
     * 通过全局事务ID、分支事务ID供业务关联try阶段的资源预留方法
     * @xid 全局事务ID
     * @bxid 分支事务ID
     */
    public void tccCancelBusi(String xid, String bxid) {
        // do cancel work
        ...... // 实现取消的业务逻辑
    }
}

6.5 XA 模式

virgo 提供对 xa 模式的支持,通过将使用的数据源包装为 virgo 提供的数据源即可。

6.6 可靠消息模式

可靠消息模式实际将操作分为两部分,首先将消息数据保存到本地,与业务执行通过全局事务统一管理,保证消息存储与业务执行的一致性,然后在事务提交阶段,将本地消息发送到消息队列中去,利用MQ机制保证消费者可以消费到消息。可靠消息可与SDT模式结合实现资源管理与恢复,可靠消息默认发送消息队列为ActiveMQ,用户需要自行配置,可靠消息模式代码示例如下:

java

@Component
public class MessageComponent() {

    //Sender为程序提供的消息发送接口,实际是先将消息保存到本地消息表中随后在事务提交阶段发送
    @Resource
    Sender sender;

    /**
     *开启全局事务入口
     */
    @VirgoTransactional
    public void doService() {
        try {
          	......//业务逻辑
            /**
             *virgo消息类型内部实现了可序列化,
             *此处为了将消息存入本地数据库,需要使用Virgo提供的消息类型创建消息
             *目前支持TextMessage/MapMessage/ObjectMessage三种类型
             */
            VirgoObjectMessage message = new VirgoObjectMessage();
            //封装消息体
            message.setObject(...);
            //调用接口,发送消息数据
            sender.send(message);
        } catch (JMSException e) {
            throw new RuntimeException(e);
        }

    }
}

7 SDT 模式语法兼容

Virgo 事务目前支持 INSERTUPDATEDELETE 三类 DML 语法的部分功能,使用Virgo请遵循如下原则:

  1. 不支持存储过程、触发器、外键;
  2. 业务表必须包含主键
  3. 不支持多表复杂 SQL;
  4. SQL 嵌套(有风险)
    • 对于含有SELECT子查询的SQL嵌套,Virgo不保证子查询的记录有锁隔离
  5. 批量 SQL
    • 预编译方式批量SQL - 支持
    • 非预编译方式批量SQL - 不支持

提示

  • 批量SQL建议不要超过2000
  • 批量SQL建议where条件中仅包含主键筛选

7.1. DML语句使用示例

类型SQL 实例是否支持
INSERTINSERT INTO tb1_name (col_name,…)
VALUES ({expr | FAULT},…),(…),…

INSERT INTO tb1_name
SET col_name={expr | DEFAULT}, …
UPDATEUPDATE tb1_name
SET col_name1=expr1 [, col_name2=expr2 …]
[WHERE where_definition]
DELETEDELETE FROM tb1_name [WHERE where_definition]
SELECTSELECT [ALL | DISTINCT | DISTINCTROW ]
select_expr, … FROM tb1_name
[WHERE where_definition]
REPLACEREPLACE [LOW_PRIORITY | DELAYED]
[INTO] tb1_name [(col_name,…)]
VALUES ({expr | DEFAULT},…),(…),…

REPLACE [LOW_PRIORITY | DELAYED]
[INTO] tb1_name
SET col_name={expr | DEFAULT}, …
MERGE INTOmerge into tb1_name a
using tb2_name b
on(a.col1=b.col1 and a.col2=b.col2 ……)
when matched then update set a.col=b.col
when not macthed then insert into a(col1,col2……)values(val1,val2……)
TRUNCATETRUNCATE [TABLE] tb1_name慎用

提示

TRUNCATE语句的执行,由于目标表数据量不可控(可能数据量非常大),导致事务执行时间不可控,故对于 TRUNCATE 语句 Virgo 将不保证数据可恢复。

7.2. SQL修饰

对于SQL修饰符的支持,Virgo分为DML语句(INSERT/DELETE/UPDATE)与DQL语句(SELECT

7.2.1. DML语句支持

类型SQL 实例是否支持
AND & ORUPDATE … WHERE col_name1=expr1 AND col_name2= expr2
LIKEUPDATE … WHERE col_name1 LIKE ‘NE’
通配符UPDATE … WHERE col_name1 LIKE ‘NE%’
BETWEENUPDATE … WHERE col_name1 BETWEEN expr1 AND expr2
ON DUPLICATEINSERT INTO tb1_name [(col_name,…)]
VALUES ({expr | DEFAULT},…),(…),…
[ ON DUPLICATE KEY UPDATE
col_name=expr, … ]

7.2.2. SELECT语句支持

类型SQL 实例NOT FOR UPDATEFOR UPDATE
AND & ORSELECT * FROM tb1_name
WHERE col_name1=expr1 AND col_name2= expr2
ORDER BYSELECT col_name1, col_name2 FROM tb1_name
ORDER BY col_name1
GROUP BYSELECT col_name1, col_name2 FROM tb1_name
GROUP BY col_name1
LIKESELECT col_name1, col_name2 FROM tb1_name
WHERE col_name1 LIKE ‘NE’
通配符SELECT col_name1, col_name2 FROM tb1_name
WHERE col_name1 LIKE ‘NE%’
EXISTSSELECT col_name1, col_name2 FROM tb1_name
WHERE EXISTS (expr1)
INSELECT col_name1, col_name2 FROM tb1_name
WHERE col_name1 IN (expr1, expr2,…)
BETWEENSELECT col_name1, col_name2 FROM tb1_name
WHERE col_name1 BETWEEN expr1 AND expr2
ON DUPLICATEINSERT INTO tb1_name [(col_name,…)]
VALUES ({expr | DEFAULT},…),(…),…
[ ON DUPLICATE KEY UPDATE col_name=expr, … ]
ALIASESSELECT t1. col_name1, t2.col_name2
FROM tb1_name AS t1, tb2_name AS t2
WHERE t1. col_name=expr AND t2. col_name=expr
TOPSELECT TOP 2 * FROM tb1_name
LIMITSELECT col_name1, col_name2 FROM tb1_name LIMIT 5
JOIN
INNER JOIN
LEFT JOIN
RIGHT JOIN
FULL JOIN
SELECT col_name1, col_name2 FROM tb1_name
JOIN tb2_name>
ON tb1_name. col_name1= tb2_name. col_name1
UNION
UNION ALL
SELECT INTO
SELECT col_name1, col_name2 FROM tb1_name
UNION
SELECT col_name1, col_name2 FROM tb2_name

7.3. 函数

类型是否支持
CONCAT(string2[,…])
INSTR(string,substring)
LCASE(string2)
LEFT(string2,length)
LENGTH(string)
LOAD_FILE(file_name)
LOCATE(substring,string[,start_position])
LPAD(string2,length,pad)
LTRIM(string2)
REPEAT(string2,count)
REPLACE(str,search_str,replace_str)
RPAD(string2,length,pad)
RTRIM(string2)
STRCMP(string1,string2)
SUBSTRING(str,position[,length])
TRIM([[BOTH|LEADING|TRAILING]FROM]string2)
UCASE(string2)
RIGHT(string2,length)
SPACE(count)
ABS(number2)
BIN(decimal_number)
CEILING(number2)
CONV(number2,from_base,to_base)
FLOOR(number2)
FORMAT(number,decimal_places)
HEX(DecimalNumber)
LEAST(number,number2[,..])
MOD(numerator,denominator)
POWER(number,power)
RAND([seed])
ROUND(number[,decimals])
SIGN(number2)
SQRT(number2)
ADDTIME(date2,time_interval)
CONVERT_TZ(datetime2,fromTZ,toTZ)
CURRENT_DATE()
CURRENT_TIME()
CURRENT_TIMESTAMP()
DATE(datetime)
DATE_ADD(date2,INTERVALd_valued_type)
DATE_FORMAT(datetime,FormatCodes)
DATE_SUB(date2,INTERVALd_valued_type)
DATEDIFF(date1,date2)
DAY(date)
DAYNAME(date)
DAYOFWEEK(date)
DAYOFYEAR(date)
EXTRACT(interval_nameFROMdate)
MAKEDATE(year,day)
MAKETIME(hour,minute,second)
MONTHNAME(date)
NOW()
SEC_TO_TIME(seconds)
STR_TO_DATE(string,format)
TIMEDIFF(datetime1,datetime2)
TIME_TO_SEC(time)
WEEK(date_time[,start_of_week])
YEAR(datetime)
DAYOFMONTH(datetime)
HOUR(datetime)
LAST_DAY(date)
MICROSECOND(datetime)
MONTH(datetime)
MINUTE(datetime)
FIRST()
LAST()
MIN()
MAX()
AVG()
SUM()
COUNT()

8 开发者须知

  1. 在不影响业务 SQL 执行结果的前提下,Virgo 默认模式可能会对执行的SQL进行修改
  2. 数据库业务表需要含有主键
  3. 建议在默认模式下,update 操作中set子句尽量不要包含函数。Virgo 支持这种语法,但对性能有一定影响
  4. 对于根据查询结果进行修改或需要查询当前已提交的数据,请使用select for update语法,否则可能查询到其它事务未提交的数据
  5. 使用 TCC 模式时,其confirmMethod以及cancelMethod必须保证幂等。即对于同一xidbxid,重复执行不应影响数据正确性
  6. TCC 模式中添加TccBranch的方法中确保只操作单一事务资源,且不会嵌套调