Appearance
virgo 客户端使用手册
1 概述
1.1 分布式事务问题
一个完整的业务往往需要调用多个子业务或服务,随着业务的不断增多,涉及的服务及数据也越来越多,越来越复杂。传统的系统难以支撑,于是出现了应用和数据库等的分布式系统。
在传统系统中,数据的一致性有数据库本身的事务机制保证。但在分布式系统中,一个完整的业务往往涉及到多个事务资源,或者会跨多个进程进行远程调用,此时数据库本身的事务机制已无法保证数据的一致性,从而产生了分布式事务的问题。
1.2 分布式事务问题的典型场景
1.2.1 跨事务资源
当应用进行分库以后,一次完整的业务访问的数据可能位于多个数据库中,但传统的数据库事务只能保证自身的事务。此时多个数据库之间的事务无法保证一致。
1.2.2 跨服务
分布式系统的一次完整的业务调用可能会跨多个进程,多个进程内的本地事务无法保证一致。
1.3 Virgo简介
Virgo 是一款用于解决上述分布式环境下的数据一致性问题分布式事务中间件。
Virgo 的架构如下所示:
Virgo 服务端:即事务协调器。负责分布式事务的推进,管理事务生命周期。
Virgo 客户端:即事务发起者。通过事务协调器,开启、提交、回滚分布式事务。同时包含部分资源管理器组件,负责管理和控制资源,与Virgo服务器进行交互。
2 使用场景
Virgo 本身可以单独与应用配合使用,也可以与 Libra 配合使用。在使用 Libra 进行分库时,为 Libra 提供分布式事务支持。
2.1 单独使用
2.1.1 跨事务资源(多个数据库)
在应用分库之后,如果需要保证多个数据库之间的数据一致,则会遇到第1.2.2节所描述的分布式事务问题。在这种情况下,使用 Virgo 如下图所示。
2.1.2 跨服务
在分布式系统中,跨服务调用时,如果需要保证多个服务之间的数据一致,则会遇到第1.2.2节所描述的分布式事务问题。在这种情况下,使用 Virgo 如下图所示。
2.2 与 Libra 配合使用
当使用 Libra 作为分布式数据中间件时,Virgo 可以与之配合,将 Libra 视为资源管理器。
2.2.1 使用 Libra 分库
如果应用系统只使用 Libra 分库,业务不会跨服务调用,且不涉及到其它事务资源时。此时可以将全局事务交由 Libra 控制,简化为如下场景。
2.2.2 跨服务+Libra
如果应用系统即使用 Libra 分库,又会跨服务调用,或涉及到其它事务资源时。此时调用如下。
或如下场景:
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 应用集成客户端及使用
本小节以基于SpringBoot
与Maven
的应用为例,使用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 客户端提供对于Spring
及Spring Boot
的适配,并对于未使用Spring
框架或需要自定义的应用提供了编程式的配置接口。
提示
对于使用了Spring
框架的应用,建议使用Spring Boot
或Spring
方式的配置
5.1 Spring Boot 配置
基于Spring Boot
的yaml
格式的配置示例如下
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 配置方式下,有两种获取事务管理器实例的方式。
- 通过 Bootstrap
java
//通过之前初始化好的 Bootstrap 实例获取,推荐使用此方法获取 TransactionManager 实例后,由应用自动控制
TransactionManager tm = bootstrap.getTransactionManager();
- 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
,TCC
及XA
三种解决方案,它们均有各自的特点及适合场景。在业务开发使用方面支持独立使用,同时兼容 Spring
、 SpringBoot
。本章将简单介绍两种事务模式以及现阶段提供的几种使用方式并给出相应使用代码示例。
6.1.1 SDT模式(默认模式)
SDT
( 模拟数据库事务
)模式是一种业务无侵入的业务解决方案。它解决了分布式事务的易用性问题,在该模式下,开发者只需关注一阶段操作,框架会自动解析SQL
语义,生成二阶段提交和回滚操作,使分布式事务的接入更便捷,该模式下对业务代码几乎无侵入,框架能够“自动化”地解决分布式架构下的数据一致性问题。 该模式下Virgo
客户端工作在JDCB API
和JDBC SPI
之间。在业务应用中发挥作用的位置如下图:
6.1.2 TCC模式
TCC模式
的解决方案其实是两阶段提交的一种改进,将整个业务逻辑的每个分支分成了Try
、Confirm
、Cancel
三个操作,其中Try部分完成业务的准备工作、Confirm部分完成业务的提交、Cancel部分完成事务的回滚。这三步仅仅是方法论,具体在每一步的操作实现,则由所涉及的服务自行设计代码实现。以简单的A向B转账为例,A加钱与B减钱的操作由两个参与方服务来实现,A和B的两个Try会同时进行业务系统检测和资源预留,只有两个Try都成功了才会往下进行Confirm操作以提交金额的增减。对于复杂的操作,还会在一个分布式事务里嵌套多层参与方,只有每层的Try都成功了,才会完成整个分布式事务的第一阶段,中间一旦任何一层失败都会回滚。 该模式的运作机理如下图:
6.1.3 XA模式
XA
模式即传统的两阶段提供协议,提供数据库层面的分布式事务原生支持。将事务提交分为prepare
与commit
两个阶段来确保分布式事务的一致性。
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
为单例对象开启分布式事务
trxManager.begin(long timeout)
timeout
为设置事务超时时间,单位毫秒,开启的事务上下文会绑定到当前线程中;
trxManager.begin(long timeout, Scope scope)
timeout
为设置事务超时时间,单位毫秒;scope
为事务作用范围;
提交分布式事务
trxManager.commit()
- 根据当前线程中的事务上下文提交分布式事务。
回滚分布式事务
trxManager.rollback()
- 根据当前线程中的事务上下文回滚分布式事务。
查询分布式事务状态
TrunkState state = trxManager.queryGlobalStatus(String xid, String dataCenter)
xid
全局事务ID;dataCenter
XID
所在的数据中心ID,双中心模式下不可为空;state
返回全局事务的状态;
6.2.5.2 线程上下文相关API
Virgo事务
是与当前线程绑定的,事务相关信息存于当前线程的线程上下文中。
TransactionContext context = TransactionContext.getContext()
- 获取当前线程上下文包含的分布式事务信息
context
为分布式事务信息载体
TransactionContext.bind(String xid)
- 分布式事务ID关联到当前线程上下文中
xid
为传入要关联的分布式事务ID
TransactionContext.unbind()
- 分布式事务ID与当前线程上下文接触关联
boolean flag = TransactionContext.inTrx()
- 查看当前线程上下文是否处于分布式事务中
TransactionContext context = TransactionContext.suspend()
- 当前线程上下文中挂起分布式事务
context
为挂起的分布式事务信息载体
TransactionContext.resume(TransactionContext resume)
- 当前线程上下文中恢复分布式事务
resume
为传入要恢复的分布式事务信息载体
6.2.5.3 事务信息相关API
Virgo用户可见的事务信息包括全局事务ID(XID)
、分支事务ID(BXID)
、分支事务模式(TrxMode)
。
以下操作是基于获取事务信息载体类TransactionContext
的操作:
TransactionContext context = TransactionContext.getContext()
String xid = context.getXid()
- 获取分布式事务XID
String bxid = context.getBxid()
- 获取分布式事务BXID
6.3 默认模式(SDT)
6.3.1 全局事务与本地事务、分支事务的关系
在Virgo中,本地事务的概念等价于Virgo分布式事务中的分支事务,以下称为分支事务。
分支事务是以Connection
的commit
执行为节点:
- 当创建的
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中事务传播行为。具体的行为影响如下所述。
PROPAGATION_REQUIRED
(最常用)原生
Spring事务
行为:支持当前本地事务,假设当前没有本地事务,就新建一个本地事务。兼容
Virgo事务
后的行为:- 如当前没有本地事务也没有全局事务,则先开启一个全局事务并新建一个本地事务;
- 如当前没有本地事务但已开启全局事务,则新建一个本地事务并加入该全局事务中;
- 如当前已有本地事务也有全局事务,则支持当前本地事务并加入该全局事务中;
- 不存在当前开启本地事务但没有开启全局事务的情况。
PROPAGATION_SUPPORTS
原生
Spring事务
行为:支持当前事务,假设当前没有事务,就以非事务方式运行。兼容
Virgo事务
后的行为:- 如当前没有本地事务也没有全局事务,则以非事务方式运行;
- 如当前没有本地事务但已开启全局事务,则不开启本地事务但将其加入全局事务中;
- 如当前已有本地事务也有全局事务,则支持当前本地事务并加入该全局事务中;
- 不存在当前开启本地事务但没有开启全局事务的情况。
PROPAGATION_MANDATORY
原生
Spring事务
行为:当前必须有本地事务,否则抛出异常。兼容
Virgo事务
后的行为:- 如当前没有本地事务也没有全局事务,则抛出异常;
- 如当前没有本地事务但已开启全局事务,则抛出异常;
- 如当前已有本地事务也有全局事务,则支持当前本地事务并加入该全局事务中;
- 不存在当前开启本地事务但没有开启全局事务的情况。
PROPAGATION_REQUIRES_NEW
原生
Spring事务
行为:新建事务,假设当前存在本地事务,则把当前本地事务挂起,重新开启一个本地事务。兼容
Virgo事务
后的行为:- 如当前没有本地事务也没有全局事务,开启一个全局事务并新建一个本地事务;
- 如当前没有本地事务但已开启全局事务,新建一个本地事务并加入到该全局事务中;
- 如当前已有本地事务也有全局事务,挂起该全局事务和本地事务,新开启一个全局事务并新建一个本地事务;
- 不存在当前开启本地事务但没有开启全局事务的情况。
PROPAGATION_NOT_SUPPORTED
原生
Spring事务
行为:以非本地事务方式运行。假设当前存在本地事务,就把当前本地事务挂起 。兼容
Virgo事务
后的行为:- 如当前没有本地事务也没有全局事务,以非事务方式运行;
- 如当前没有本地事务但已开启全局事务,将全局事务挂起,本地以非事务方式运行;
- 如当前已有本地事务也有全局事务,将全局事务与本地事务挂起,以非事务方式运行;
- 不存在当前开启本地事务但没有开启全局事务的情况。
PROPAGATION_NEVER
原生
Spring事务
行为:以非事务方式运行,假设当前存在事务,则抛出异常。兼容
Virgo事务
后的行为:- 如当前没有本地事务也没有全局事务,以非事务方式运行;
- 如当前没有本地事务但已开启全局事务,抛出异常;
- 如当前已有本地事务也有全局事务,抛出异常;
- 不存在当前开启本地事务但没有开启全局事务的情况。
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 事务目前支持 INSERT
、UPDATE
、DELETE
三类 DML 语法的部分功能,使用Virgo请遵循如下原则:
- 不支持存储过程、触发器、外键;
- 业务表必须包含主键
- 不支持多表复杂 SQL;
- SQL 嵌套(有风险)
- 对于含有
SELECT
子查询的SQL嵌套,Virgo不保证子查询的记录有锁隔离
- 对于含有
- 批量 SQL
预编译
方式批量SQL - 支持非预编译
方式批量SQL - 不支持
提示
- 批量SQL建议不要超过2000
- 批量SQL建议where条件中仅包含主键筛选
7.1. DML语句使用示例
类型 | SQL 实例 | 是否支持 |
---|---|---|
INSERT | INSERT INTO tb1_name (col_name,…) VALUES ({expr | FAULT},…),(…),… 或 INSERT INTO tb1_name SET col_name={expr | DEFAULT}, … | 是 |
UPDATE | UPDATE tb1_name SET col_name1=expr1 [, col_name2=expr2 …] [WHERE where_definition] | 是 |
DELETE | DELETE FROM tb1_name [WHERE where_definition] | 是 |
SELECT | SELECT [ALL | DISTINCT | DISTINCTROW ] select_expr, … FROM tb1_name [WHERE where_definition] | 是 |
REPLACE | REPLACE [LOW_PRIORITY | DELAYED] [INTO] tb1_name [(col_name,…)] VALUES ({expr | DEFAULT},…),(…),… 或 REPLACE [LOW_PRIORITY | DELAYED] [INTO] tb1_name SET col_name={expr | DEFAULT}, … | 否 |
MERGE INTO | merge 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……) | 否 |
TRUNCATE | TRUNCATE [TABLE] tb1_name | 慎用 |
提示
TRUNCATE
语句的执行,由于目标表数据量不可控(可能数据量非常大),导致事务执行时间不可控,故对于 TRUNCATE
语句 Virgo 将不保证数据可恢复。
7.2. SQL修饰
对于SQL修饰符的支持,Virgo分为DML
语句(INSERT
/DELETE
/UPDATE
)与DQL
语句(SELECT
)
7.2.1. DML语句支持
类型 | SQL 实例 | 是否支持 |
---|---|---|
AND & OR | UPDATE … WHERE col_name1=expr1 AND col_name2= expr2 | 是 |
LIKE | UPDATE … WHERE col_name1 LIKE ‘NE’ | 是 |
通配符 | UPDATE … WHERE col_name1 LIKE ‘NE%’ | 是 |
BETWEEN | UPDATE … WHERE col_name1 BETWEEN expr1 AND expr2 | 是 |
ON DUPLICATE | INSERT INTO tb1_name [(col_name,…)] VALUES ({expr | DEFAULT},…),(…),… [ ON DUPLICATE KEY UPDATE col_name=expr, … ] | 否 |
7.2.2. SELECT
语句支持
类型 | SQL 实例 | NOT FOR UPDATE | FOR UPDATE |
---|---|---|---|
AND & OR | SELECT * FROM tb1_name WHERE col_name1=expr1 AND col_name2= expr2 | 是 | 是 |
ORDER BY | SELECT col_name1, col_name2 FROM tb1_name ORDER BY col_name1 | 是 | 是 |
GROUP BY | SELECT col_name1, col_name2 FROM tb1_name GROUP BY col_name1 | 是 | 是 |
LIKE | SELECT 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%’ | 是 | 是 |
EXISTS | SELECT col_name1, col_name2 FROM tb1_name WHERE EXISTS (expr1) | 是 | 是 |
IN | SELECT col_name1, col_name2 FROM tb1_name WHERE col_name1 IN (expr1, expr2,…) | 是 | 是 |
BETWEEN | SELECT col_name1, col_name2 FROM tb1_name WHERE col_name1 BETWEEN expr1 AND expr2 | 是 | 是 |
ON DUPLICATE | INSERT INTO tb1_name [(col_name,…)] VALUES ({expr | DEFAULT},…),(…),… [ ON DUPLICATE KEY UPDATE col_name=expr, … ] | 是 | 是 |
ALIASES | SELECT 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 | 是 | 是 |
TOP | SELECT TOP 2 * FROM tb1_name | 是 | 是 |
LIMIT | SELECT 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 开发者须知
- 在不影响业务 SQL 执行结果的前提下,Virgo 默认模式可能会对执行的SQL进行修改
- 数据库业务表需要含有主键
- 建议在默认模式下,update 操作中
set
子句尽量不要包含函数。Virgo 支持这种语法,但对性能有一定影响 - 对于根据查询结果进行修改或需要查询当前已提交的数据,请使用
select for update
语法,否则可能查询到其它事务未提交的数据 - 使用 TCC 模式时,其
confirmMethod
以及cancelMethod
必须保证幂等。即对于同一xid
与bxid
,重复执行不应影响数据正确性 - TCC 模式中添加
TccBranch
的方法中确保只操作单一事务资源,且不会嵌套调