全部产品
云市场

DRDS 分布式事务

更新时间:2019-04-18 10:17:40

在分布式数据库中,如果一个事务同时修改多个分库的数据,无法简单保证所有分库一定都能提交成功。如果在事务提交过程中出现异常(例如:主备切换等),可能出现一些分库提交成功、另一些分库提交失败的情况,导致数据不一致。

DRDS 为用户提供如下多种分布式事务策略,您可以根据实际使用场景,选择合适的分布式事务策略。

在使用 RDS for MySQL 5.7 版本下,DRDS 默认全局开启 XA 强一致分布式事务,业务端无需任何感知,与单机数据库体验一致。

在使用 RDS for MySQL 5.5 与 5.6 版本下,DRDS 默认情况下禁止用户在同一事务中对多个库做修改,需要手动开启分布式事务,参考本文下方”如何设置 DRDS 分布式事务策略”。

策略 描述 特性 适用场景
FREE 允许多写 不保证原子性,无性能损耗 数据批量导入、表初始化等场景
2PC 两阶段提交事务 保证原子性,不保证可见性 推荐 RDS for MySQL 5.6 用户使用
XA 强一致分布式事务 保证原子性,保证可见性 DRDS 配合 RDS for MySQL 5.7 默认全局开启,推荐使用
FLEXIBLE 柔性事务 补偿型事务 适用于性能要求较高、高并发的业务场景

FREE 允许多写

FREE 策略下,DRDS 简单地允许跨库写入操作。

如果事务提交过程中发生主备库切换、网络分区等异常,可能出现部分分库提交成功、部分失败的不一致情况,故不推荐在任何线上业务中使用,仅用于数据导入等场景。

如果数据导入中途出错,您可以使用 INSERT IGNORE 忽略已导入的数据,或清空(TRUNCATE)数据表之后重新导入。

2PC 事务

两阶段提交(2PC)事务中,DRDS 在 PREPARE 时将用户 SQL 持久化记录在 REDO_LOG 表。如果事务提交过程中发生异常,中断的分库事务会通过 REDO_LOG 中的记录进行重做,尽最大努力保证各分库都能提交,整体达到“已提交”的一致状态。

极少数情况下(例如 RDS 宕机)可能出现用户 SQL 先于 REDO_LOG 执行,可能导致脏写现象。

XA 强一致事务

XA 事务利用 RDS for MySQL XA 分布式事务协议,提供了强一致的分布式事务支持,能获得最接近单机事务的使用体验。

XA 强一致分布式事务需要 RDS for MySQL 5.7 及以上版本支持,默认全局开启,业务层无需任何感知

如果您在使用 RDS for MySQL 5.6 或更早的版本,建议使用 2PC 事务作为替代,或升级到 RDS for MySQL 5.7。

FLEXIBLE 柔性事务

柔性事务在涉及多个分库时,将根据 SQL 语句的含义自动生成相应的补偿操作。一旦发生部分分库提交成功、部分分库失败的情况,DRDS 用记录的补偿操作撤消之前的修改,从而保证事务的原子性,实现数据的最终一致。

更多资料请参考 DRDS 柔性事务

如何设置 DRDS 分布式事务策略

在使用 MySQL 5.7 的前提下,DRDS 会自动按需开启 XA 分布式事务,与单机数据库体验一致,无需业务代码做特殊处理。

如何通过 JDBC 使用 DRDS 分布式事务?

在事务开启后,执行 SET drds_transaction_policy = '...' 即可。

Java 代码示例(以 FLEXIBLE 事务为例):

  1. conn.setAutoCommit(false);
  2. try (Statement stmt = conn.createStatement()) {
  3. stmt.execute("SET drds_transaction_policy = 'FLEXIBLE'"); // 以 FLEXIBLE 为例
  4. }
  5. // ... 运行业务 SQL ...
  6. conn.commit(); // 或 rollback()

注意:

  1. 只允许在开启事务(autocommit = off)之后立即设置 drds_transaction_policy 属性,否则将会报错。
  2. 当前会话中设置的 drds_transaction_policy 属性会在 SET autocommit = on 后自动重置。

如何在 Spring 框架中使用 DRDS 分布式事务?

如果使用 Spring 的 @Transactional 注解开启事务,可以通过扩展事务管理器来开启 DRDS 分布式事务。

代码示例:

  1. import org.springframework.jdbc.datasource.DataSourceTransactionManager;
  2. import org.springframework.transaction.TransactionDefinition;
  3. import javax.sql.DataSource;
  4. import java.sql.Connection;
  5. import java.sql.SQLException;
  6. import java.sql.Statement;
  7. public class DrdsTransactionManager extends DataSourceTransactionManager {
  8. public DrdsTransactionManager(DataSource dataSource) {
  9. super(dataSource);
  10. }
  11. @Override
  12. protected void prepareTransactionalConnection(Connection con, TransactionDefinition definition) throws SQLException {
  13. try (Statement stmt = con.createStatement()) {
  14. stmt.executeUpdate("SET drds_transaction_policy = 'FLEXIBLE'"); // 以 FLEXIBLE 为例
  15. }
  16. }
  17. }

之后,在 Spring 配置中将上述类实例化,例如:

  1. <bean id="drdsTransactionManager" class="my.app.DrdsTransactionManager">
  2. <property name="dataSource" ref="yourDataSource" />
  3. </bean>

对于需要开启事务的类,加上注解 @Transactional("drdsTransactionManager") 即可。