本文介绍使用 AT 模式接入 GTS 时的注意事项。

单个事务内操作记录建议少于 200 条

虽然 GTS 本身支持大事务,可以支持 1 万行的数据规模,但建议使用时不要超过 200 条。原因有以下两方面。

  • 造成事务执行时间上升,事务信息规模扩大,导致系统性能下降。
  • 造成潜在的事务数据不一致问题。如果有特殊业务,需要单个事务超过 1000 条 SQL 语句,请提交工单申请。

事务隔离级别

GTS 的缺省事务隔离级别为读未提交,该模式下可以达到分布式事务的最大性能。如果有特殊业务,需要隔离级别为读已提交,可以使用 SQL 的 for update。

注意 读已提交的 for update 必须在一个 GTS 的分布式事务内部才能生效。

GTS 的读已提交用法示例如下:

SELECT name FROM tb WHERE id =1 FOR UPDATE;            
说明 如果您需要读已提交以上的隔离级别,或者您对 GTS 的读未提交有疑虑,请提交工单申请,GTS 技术支持人员会协助您优化您的事务和相关 SQL。

确保 GTS 事务管理范围内的所有数据不会被 GTS 管理范围外的系统修改

GTS 的全局数据库写锁仅针对加入了 GTS 事务的数据库操作,如果一个对数据库的写操作不在 GTS 事务管理的范围内,会造成“脏写”。例如,一个写操作在 GTS 事务中对一条数据进行了修改,但是尚未提交,使用 MySQL 控制台(不在 GTS 事务范围内)对该数据也进行了修改,而此时业务驱动前面的 GTS 事务回滚,这次回滚会失败,造成数据不一致。

关闭 Druid 连接池的 SQL 缓存功能

配合 EDAS、DRDS 共同使用的场景中,GTS 事务上下文通过 SQL hint 的形式下发到 DRDS。

在 Druid 开启 PreparedStatement 缓存功能后,Druid 会缓存前一次执行的包含事务上下文的 SQL 语句。在另一个事务进行时,发送这个包含过期事务上下文的 SQL 就会被认为无效而发生错误。

所以,这种场景中,需要关闭 Druid 连接池的 SQL 缓存功能。方法如下:

<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init">

    <!-- 其他property略 -->
    <!-- 同时配置下面两个值来关闭PS Cache -->
    <!-- poolPreparedStatements = false -->
    <property name="poolPreparedStatements" value="false" />
    <!-- maxPoolPreparedStatementPerConnectionSize = 0 -->
    <property name="maxPoolPreparedStatementPerConnectionSize" value="0" />
</bean>     

数据源配置

GTS 可以支持 MySQL、DRDS、Oracle、RDS、PostgreSQL 等数据库。在访问不同类型数据库时,配置也有所不同。

  • 访问 5.3 以下版本的 DRDS 数据库

    当应用通过 GTS 访问 DRDS 数据库时,不能使用 TxcDataSource 数据源,可以使用 JDBC 数据源。

    配置方式如下:

    <bean id="DataSource1" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="url" value="jdbc:mysql://xxx:3306/xxx" />
        <property name="username" value="xxx" />
        <property name="password" value="xxx" />
        <property name="driverClassName" value="com.mysql.jdbc.Driver" />
    </bean>           
  • 访问 5.3 及以上版本的 DRDS 或其他关系数据库

    当应用通过 GTS 访问非 DRDS 数据库时,如 MySQL、DRDS(5.3及以上版本)、Oracle、RDS、PostgreSQL 等,必须要使用 txc 的数据源。

    • 直接使用 txc 数据源,配置方式如下:

      <bean id="DataSource_rds" class="com.taobao.txc.datasource.cobar.TxcDataSource">
           <property name="url" value="jdbc:mysql://xxxxx" />
           <property name="username" value="xxx" />
           <property name="password" value="xxx" />
           <property name="driverClassName" value="com.mysql.jdbc.Driver" />
       </bean>                    
    • 使用 Druid 数据源,配置方式如下:

      <bean id="txcDataSource" class="com.taobao.txc.datasource.cobar.TxcDataSource">
             <constructor-arg ref="druidDataSource"/>
         </bean>
      
         <bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init">
             <property name="url" value="jdbc:mysql://xxxxxxxxx:3306/xxx"/>
             <property name="username" value="xxx"/>
             <property name="connectProperties">
                 <props>
                     <prop key="password">xxx</prop>
                 </props>
             </property>
             <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
             <property name="filters" value="stat" />
             <property name="maxActive" value="300" />
             <property name="initialSize" value="10" />
             <property name="maxWait" value="60000" />
             <property name="minIdle" value="1" />
             <property name="timeBetweenEvictionRunsMillis" value="60000" />
             <property name="minEvictableIdleTimeMillis" value="300000" />
             <property name="validationQuery" value="SELECT 'x'" />
             <property name="testWhileIdle" value="true" />
             <property name="testOnBorrow" value="false" />
             <property name="testOnReturn" value="false" />
             <property name="poolPreparedStatements" value="false" />
             <property name="maxPoolPreparedStatementPerConnectionSize" value="0" />
         </bean>