ShardingSphere 事务模块的附录内容。这部分通常包含一些重要的补充信息、配置细节、命令和最佳实践提示,对于实际应用和问题排查非常关键。
根据提供的链接(/features/transaction/appendix/
),我们深入探讨其核心内容:
附录核心内容解析
附录通常不会引入全新概念,而是对主文档的扩展和细化。以下是 ShardingSphere 事务附录中可能包含的关键信息(结合其设计理念和常见实践):
1. XA 事务恢复命令 (XA Recovery Commands)
* **目的:** 当出现网络分区、节点宕机或其他故障导致 XA 事务处于 `PREPARED` (悬挂) 状态时,数据库管理员 (DBA) 需要手动介入查询和解决这些事务。
* **关键数据库命令:**
* **MySQL:**
* `XA RECOVER;`: 列出当前 MySQL 实例中所有处于 `PREPARED` 状态的 XA 事务。输出包含 `formatID`, `gtrid_length`, `bqual_length`, `data` (最重要的字段,通常包含 XID) 和状态。
* `XA COMMIT <XID> [ONE PHASE];`: 提交指定的悬挂事务。`ONE PHASE` 仅在事务只涉及一个 RM 且未进入 `PREPARED` 状态时可用(通常不适用)。
* `XA ROLLBACK <XID>;`: 回滚指定的悬挂事务。
* **PostgreSQL:**
* `SELECT * FROM pg_prepared_xacts;`: 查询当前数据库中的所有预备(`PREPARED`)事务。返回事务 ID (`transaction`)、全局事务 ID (`gid`)、准备时间、数据库名、用户名等信息。
* `COMMIT PREPARED '<gid>';`: 提交指定全局事务 ID (`gid`) 的预备事务。
* `ROLLBACK PREPARED '<gid>';`: 回滚指定全局事务 ID (`gid`) 的预备事务。
* **Oracle:** 恢复通常更依赖于 Oracle 自身的恢复机制和 DBA 工具 (如 `DBA_2PC_PENDING` 视图, `DBMS_XA` 包),操作需极其谨慎。
* **ShardingSphere XA TM 日志:** 除了数据库端的悬挂事务,ShardingSphere 内嵌的 TM (Narayana/Atomikos) 也会在持久化存储(文件或数据库)中记录事务日志。恢复时 TM 会优先根据这些日志驱动恢复。**附录应强调配置可靠 TM 日志存储的重要性及其位置。**
* **操作警告:** **极其谨慎!** 手动提交/回滚悬挂事务可能导致数据不一致。操作前必须:
* 确认该事务确实无法由 TM 自动恢复。
* 尽可能联系应用开发者确认该事务的业务语义。
* 优先尝试重启应用/ShardingSphere Proxy 以触发 TM 自动恢复。
* 在非生产环境充分练习。
2. Seata AT 模式集成详细配置示例
* **目的:** 提供更完整的、可直接参考的 Seata AT 集成配置片段,超越基础开关。
* **可能包含的配置项:**
* **`seata.tx-service-group`:** 事务服务组名,通常与应用名 (`spring.application.name`) 关联映射到 Seata 的 `default` 集群或其他自定义集群。`sharding-service-tx-group`。
* **`seata.service.vgroup-mapping.${tx-service-group}`:** 明确指定事务服务组映射到哪个 TC 集群。`sharding-service-tx-group=default` (映射到名为 `default` 的 TC 集群)。
* **`seata.service.grouplist.default`:** 指定 `default` TC 集群的服务器地址列表。`192.168.0.1:8091,192.168.0.2:8091`。
* **`seata.enable-auto-data-source-proxy=false` (重要!):** **必须显式关闭** Seata 原生 DataSource 代理,避免与 ShardingSphere 的代理冲突。这是集成时最常见的坑之一。
* **`seata.client.tm.degrade-check` / `seata.client.tm.degrade-check-allow-times`:** 开启 TM 降级检查(当 TC 不可用达到阈值时,后续事务默认走本地事务,不再尝试全局事务),并设置允许次数。
* **`seata.client.rm.report.retry-count`:** RM 向 TC 报告分支状态的失败重试次数。
* **`seata.client.rm.asyn-commit-buffer-limit` / `seata.client.rm.lock.retry-interval` / `seata.client.rm.lock.retry-times`:** 性能与锁相关调优参数。
* **`seata.registry.type` / `seata.config.type`:** 如果 TC 使用了非 file 的注册中心(如 Nacos, Eureka)或配置中心(如 Nacos, Apollo),需在此配置。ShardingSphere 通常不管理 TC 的注册/配置,但应用需要知道如何连接 TC。
* **Spring Boot Starter 配置示例 (YAML):**
```yaml
seata:
enabled: true
application-id: ${spring.application.name} # 通常与应用名一致
tx-service-group: ${spring.application.name}-tx-group # 事务组名,常基于应用名
service:
vgroup-mapping:
${seata.tx-service-group}: default # 映射到 default TC集群
grouplist:
default: 127.0.0.1:8091 # TC Server 地址
client:
rm:
report-success-enable: false # 是否上报分支成功,默认false即可
async-commit-buffer-limit: 10000 # 异步提交缓存上限
lock:
retry-interval: 10ms # 全局锁重试间隔
retry-times: 30 # 全局锁重试次数
tm:
degrade-check: true # 开启 TM 降级检查
degrade-check-allow-times: 10 # TC 不可用多少次后触发降级
# 关键!关闭 Seata 原生数据源代理
enable-auto-data-source-proxy: false
```
3. BASE 模式 (本地事务表) 详细配置与实现要求
* **事务日志表 DDL 示例:** 提供标准的事务日志表创建 SQL,明确字段含义和索引要求。
```sql
CREATE TABLE IF NOT EXISTS transaction_log (
id VARCHAR(128) NOT NULL, -- 全局唯一事务ID (XID)
transaction_id VARCHAR(255) NOT NULL, -- 可能的分支事务关联ID或冗余
log_status VARCHAR(20) NOT NULL, -- 状态: INIT, SUCCESS, FAILURE
log_detail TEXT, -- 可选,存储额外信息如错误原因
create_time TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
PRIMARY KEY (id),
INDEX idx_create_time (create_time) -- 方便异步作业按时间扫描
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
```
* **`TransactionLogEventListener` 接口实现示例:** 展示一个基本的将事务日志事件发布到消息队列 (如 Kafka) 的实现。
```java
@Component
public class KafkaTransactionLogEventListener implements TransactionLogEventListener {
@Autowired
private KafkaTemplate<String, String> kafkaTemplate;
@Override
public void onEvent(TransactionLogEvent event) {
String xid = event.getId();
String status = event.getLogStatus();
String payload = ...; // 构建包含 xid, status, 可能的分片信息等的消息体
kafkaTemplate.send("transaction-log-topic", xid, payload)
.addCallback(...); // 处理发送成功/失败,需考虑重试
}
}
```
* **异步作业配置提示:** 提醒用户需要实现一个后台作业(如 Spring Scheduler, Quartz, ElasticJob)来:
1. 扫描 `transaction_log` 表中状态为 `INIT` 的记录。
2. 根据 XID 查询**所有相关分片**上该事务的日志记录状态。
3. 如果**所有记录状态为 `SUCCESS`**,则触发 **Confirm 逻辑**(执行业务确认操作)。
4. 如果**任意记录状态为 `FAILURE`**,则触发 **Cancel 逻辑**(执行业务补偿操作)。
5. 更新事务日志状态(如标记为 `PROCESSED` 或直接删除/归档),防止重复处理。**补偿/确认逻辑必须幂等!**
* **`props` 配置项详解:**
* `store-db`: 指定事务日志表存储在哪个逻辑库(默认当前操作的逻辑库)。
* `store-table`: 指定事务日志表的名称(默认 `transaction_log`)。
* `scan-frequency`: 提示用户配置其异步作业的扫描频率(非 ShardingSphere 直接管理)。
4. 分布式事务 ID (XID) 传递与上下文管理
* **跨服务传递 (RPC):** 强调在微服务架构中,使用 Seata AT 或需要手动管理 BASE 事务时,**必须在 RPC 调用中传播 XID**。提供常见 RPC 框架的集成示例:
* **Dubbo:** 通过 `RpcContext` 的 `attachment` 传递。Seata 的 `dubbo` 模块已提供 `TransactionPropagationFilter`。
* **Spring Cloud OpenFeign:** 实现 `RequestInterceptor`,将 XID 放入请求头 (如 `Seata-Xid: ${xid}`)。Seata 的 `spring-cloud-starter-seata` 通常已包含。
* **gRPC:** 通过 gRPC 的 `Metadata` (Header) 传递。
* **线程池/异步任务传递:** 提醒用户在使用线程池或异步框架(如 `@Async`, CompletableFuture)执行分布式事务内的操作时,**需要手动传递 XID**。示例使用 `TransactionalContext` (ShardingSphere) 或 `RootContext` (Seata):
```java
// 在主线程捕获上下文
String xid = TransactionalContext.getXID(); // 或 RootContext.getXID()
// 在异步任务中绑定上下文
executorService.submit(() -> {
TransactionalContext.bind(xid); // 或 RootContext.bind(xid);
try {
// 执行需要参与分布式事务的业务逻辑
} finally {
TransactionalContext.unbind(); // 或 RootContext.unbind();
}
});
```
5. 事务管理器 SPI 扩展 (高级)
* **接口:** `org.apache.shardingsphere.transaction.spi.ShardingSphereTransactionManager`
* **目的:** 允许用户集成自定义的或第三方未内置支持的分布式事务管理器(如另一种 Saga 实现)。
* **关键方法:**
* `void init(DatabaseType databaseType, Collection<ResourceDataSource> resourceDataSources, String providerType)`: 初始化。
* `TransactionType getTransactionType()`: 返回支持的事务类型 (如 `TransactionType.XA`, `BASE`, 或自定义类型)。
* `boolean isInTransaction()`: 是否在事务中。
* `Connection getConnection(String dataSourceName) throws SQLException`: 获取一个纳入事务管理的连接。
* `void begin()`: 开启事务。
* `void commit()`: 提交事务。
* `void rollback()`: 回滚事务。
* `void close()`: 关闭资源。
* **实现步骤:**
1. 实现 `ShardingSphereTransactionManager` 接口。
2. 在 `META-INF/services` 目录下创建文件 `org.apache.shardingsphere.transaction.spi.ShardingSphereTransactionManager`,内容为自定义实现类的全限定名。
3. 配置 `transactionType` 为自定义类型的名称 (需与 `getTransactionType()` 返回的一致)。
6. 版本兼容性与变更说明
* **重要提示:** 明确说明当前文档描述的配置项、API、行为对应的是哪个 ShardingSphere 版本(例如 5.3.2+)。
* **关键变更:** 如果附录内容涉及与之前版本的重大变更(如配置项改名、废弃 API、行为变化),需要清晰标注。例如:
* `sharding.transaction.type` 在 5.x 中可能已迁移到 `rules` 下的 `transaction` 规则中。
* Seata 集成所需的依赖包名或配置前缀可能在版本升级后发生变化。
学习附录的关键要点
- 面向实践与运维: 附录的内容通常是为了解决“如何具体做?”(配置、命令)和“出了问题怎么办?”(恢复命令)的问题。
- 细节决定成败: XA 恢复命令的语法、Seata 集成中
enable-auto-data-source-proxy: false
、BASE 模式的事务日志表结构和异步作业实现,这些细节配置错误会直接导致功能失效或生产事故。 - 重视上下文传播: 分布式事务在微服务和异步编程环境下工作的基石是 XID 的正确传播。附录应提供清晰的跨线程、跨服务传递指南。
- 高级扩展性: SPI 接口的存在表明了 ShardingSphere 事务模块的可扩展性,为满足特殊需求提供了途径(尽管实现成本较高)。
- 版本意识: 务必核对使用的 ShardingSphere 版本与文档版本是否匹配,留意配置和 API 的变更。
下一步行动建议:
- 动手验证恢复命令: 在测试环境,模拟 XA 悬挂事务,练习使用
XA RECOVER
/pg_prepared_xacts
查询和手动COMMIT
/ROLLBACK
。理解输出格式。 - 搭建完整 Seata 集成 Demo: 严格按照附录和主文档配置一个包含 Seata TC Server 的 Demo,特别注意
enable-auto-data-source-proxy: false
。测试正常和异常流程。 - 实现 BASE 模式全链路: 创建事务日志表,实现
TransactionLogEventListener
(如发送 Kafka 消息),编写一个简单的异步消费者作业模拟 Confirm/Cancel 逻辑。测试最终一致性延迟。 - 模拟 XID 传递: 在包含 RPC 调用的微服务 Demo 中,验证 XID 是否通过 HTTP Header 或 Dubbo Attachment 正确传递到了下游服务,并参与到了同一个全局事务中。
- 查阅 SPI 源码: 浏览
ShardingSphereTransactionManager
SPI 接口和其内置实现 (XA
,SeataAT
,Base
),理解其设计,思考可能的扩展场景。
深入理解并实践附录中的内容,将使你不仅能够配置和使用 ShardingSphere 的事务功能,更能具备在生产环境中运维、诊断和扩展它的能力。继续加油!