二、数据持久化篇(深度增强版)

125 阅读11分钟

二、数据持久化篇(深度增强版)

2.1 JDBC Template深度解析

架构设计思想
graph LR
    A[DataSource] --> B[JdbcTemplate]
    B --> C[执行SQL]
    C --> D[处理结果集]
    D --> E[转换业务对象]
    
    style B fill:#f9f,stroke:#333
    style C fill:#ccf,stroke:#333

设计模式解析

  1. 模板方法模式
    • 封装固定流程:获取连接→准备语句→执行→处理结果→释放资源
    • 开发者只需关注SQL和结果处理
  2. 回调机制
    • 通过RowMapper实现结果集到对象的映射
    • 使用PreparedStatementCreator定制语句创建

企业级查询优化

// 分页查询最佳实践
public Page<User> findUsersByPage(int pageNum, int pageSize) {
    String countSql = "SELECT COUNT(*) FROM users";
    int total = jdbcTemplate.queryForObject(countSql, Integer.class);
    
    String dataSql = "SELECT * FROM users LIMIT ? OFFSET ?";
    List<User> content = jdbcTemplate.query(
        dataSql,
        new Object[]{pageSize, (pageNum-1)*pageSize},
        new BeanPropertyRowMapper<>(User.class)
    );
    
    return new Page<>(content, pageNum, pageSize, total);
}

// 查询结果缓存方案
@Cacheable(value = "users", key = "#name")
public User findByName(String name) {
    return jdbcTemplate.queryForObject(
        "SELECT * FROM users WHERE name = ?",
        new Object[]{name},
        new BeanPropertyRowMapper<>(User.class)
    );
}

2.2 事务管理引擎(原理级解析)

事务传播机制本质
// 传播机制伪代码实现
public void executeWithTransaction(TransactionDefinition definition) {
    TransactionStatus status = null;
    try {
        // 判断当前是否存在事务
        boolean existingTransaction = isExistingTransaction();
        
        // 根据传播行为决定事务边界
        if (definition.getPropagationBehavior() == PROPAGATION_REQUIRED) {
            if (!existingTransaction) {
                status = startNewTransaction(definition);
            } else {
                status = participateInExistingTransaction();
            }
        }
        
        // 执行业务逻辑
        businessLogic();
        
        // 提交或回滚
        if (status != null && !status.isCompleted()) {
            commitTransaction(status);
        }
    } catch (Exception ex) {
        handleRollback(status, ex);
        throw ex;
    }
}

隔离级别对比表

隔离级别脏读不可重复读幻读性能影响
READ_UNCOMMITTED
READ_COMMITTED×
REPEATABLE_READ××较高
SERIALIZABLE×××

生产环境配置建议

spring:
  datasource:
    hikari:
      isolation-level: TRANSACTION_REPEATABLE_READ
  jpa:
    properties:
      hibernate:
        connection:
          # 设置MySQL实际隔离级别
          isolation_level: 4  # 对应REPEATABLE_READ

2.3 MyBatis整合方案(原理与优化)

执行过程剖析
sequenceDiagram
    participant A as Mapper接口
    participant B as SqlSession
    participant C as Executor
    participant D as StatementHandler
    participant E as JDBC
    
    A->>B: 调用接口方法
    B->>C: 获取Executor
    C->>D: 创建StatementHandler
    D->>E: 预编译SQL
    E->>D: 返回结果集
    D->>C: 结果处理
    C->>B: 返回处理结果
    B->>A: 返回业务对象

二级缓存优化策略

<!-- 启用二级缓存 -->
<settings>
    <setting name="cacheEnabled" value="true"/>
</settings>

<!-- Mapper级别配置 -->
<mapper namespace="com.example.UserMapper">
    <cache eviction="LRU" 
           flushInterval="60000"
           size="1024"
           readOnly="true"/>
</mapper>

插件开发实战

// SQL执行时间监控插件
@Intercepts({
    @Signature(type = Executor.class,
              method = "query",
              args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
    @Signature(type = Executor.class,
              method = "update",
              args = {MappedStatement.class, Object.class})
})
public class PerformanceInterceptor implements Interceptor {
    private static final Logger logger = LoggerFactory.getLogger(PerformanceInterceptor.class);

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
        String sqlId = mappedStatement.getId();
        long start = System.currentTimeMillis();
        
        try {
            return invocation.proceed();
        } finally {
            long cost = System.currentTimeMillis() - start;
            logger.info("SQL [{}] 执行耗时: {}ms", sqlId, cost);
            Metrics.counter("sql_query_count").increment();
            Metrics.timer("sql_duration").record(cost, TimeUnit.MILLISECONDS);
        }
    }
}

扩展说明总结

  1. JDBC Template设计哲学

    • 通过模板方法消除样板代码
    • 分离资源管理与业务逻辑
    • 适合需要精细控制SQL的场景
  2. 事务管理的本质

    • 通过AOP实现声明式事务
    • 传播机制本质是事务上下文的传递策略
    • 隔离级别需要与数据库实际级别对齐
  3. MyBatis最佳实践

    • 动态SQL适合复杂查询场景
    • 二级缓存适合读多写少的业务
    • 插件机制可扩展监控能力
  4. 性能优化方向

    graph TD
        A[数据持久化优化] --> B[连接池配置]
        A --> C[批处理操作]
        A --> D[合理使用缓存]
        A --> E[索引优化]
        B --> F[最大连接数]
        B --> G[超时配置]
        C --> H[批量插入]
        C --> I[批量更新]
        D --> J[一级缓存]
        D --> K[二级缓存]
    

2.4 JPA规范实践(高效ORM解决方案)

2.4.1 JPA核心概念与实体映射

实体类映射规范

@Entity
@Table(name = "orders", indexes = {
    @Index(name = "idx_order_user", columnList = "user_id"),
    @Index(name = "idx_order_status", columnList = "status")
})
public class Order {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false, length = 50)
    private String orderNumber;

    @Enumerated(EnumType.STRING)
    @Column(length = 20)
    private OrderStatus status;

    @CreationTimestamp
    private LocalDateTime createTime;

    @UpdateTimestamp
    private LocalDateTime updateTime;

    // 关联关系配置
    @OneToMany(mappedBy = "order", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<OrderItem> items = new ArrayList<>();

    // 省略getter/setter
}

Repository接口智能方法推导

public interface OrderRepository extends JpaRepository<Order, Long> {
    
    // 根据状态分页查询
    Page<Order> findByStatus(OrderStatus status, Pageable pageable);

    // 复杂条件查询
    @Query("SELECT o FROM Order o WHERE " +
           "o.createTime BETWEEN :start AND :end " +
           "AND o.totalAmount > :minAmount")
    List<Order> findRecentHighValueOrders(
        @Param("start") LocalDateTime start,
        @Param("end") LocalDateTime end,
        @Param("minAmount") BigDecimal minAmount);

    // 动态查询
    interface OrderSpec {
        static Specification<Order> hasStatus(OrderStatus status) {
            return (root, query, cb) -> 
                cb.equal(root.get("status"), status);
        }
        
        static Specification<Order> createdAfter(LocalDateTime time) {
            return (root, query, cb) -> 
                cb.greaterThan(root.get("createTime"), time);
        }
    }

    List<Order> findAll(Specification<Order> spec, Sort sort);
}

2.4.2 关联关系映射实战(电商订单系统案例)

一对多关系配置

@Entity
public class OrderItem {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "order_id", nullable = false)
    private Order order;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "product_id", nullable = false)
    private Product product;

    @Column(nullable = false)
    private Integer quantity;

    // 省略其他字段
}

// 级联操作示例
Order order = new Order();
order.addItem(new OrderItem(product1, 2));
order.addItem(new OrderItem(product2, 1));
orderRepository.save(order); // 自动保存所有关联项

多对多关系配置

@Entity
public class Product {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToMany
    @JoinTable(name = "product_category",
        joinColumns = @JoinColumn(name = "product_id"),
        inverseJoinColumns = @JoinColumn(name = "category_id"))
    private Set<Category> categories = new HashSet<>();
}

@Entity
public class Category {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToMany(mappedBy = "categories")
    private Set<Product> products = new HashSet<>();
}

2.4.3 N+1查询问题解决方案(性能优化关键)

问题复现与诊断

// 危险查询方式
List<Order> orders = orderRepository.findAll();
orders.forEach(order -> {
    System.out.println(order.getItems().size()); // 每次访问触发查询
});

优化方案一:@EntityGraph立即加载

public interface OrderRepository extends JpaRepository<Order, Long> {
    @EntityGraph(attributePaths = {"items", "items.product"})
    @Query("SELECT o FROM Order o WHERE o.id = :id")
    Optional<Order> findByIdWithDetails(@Param("id") Long id);
}

// 生成SQL:
SELECT o.*, i.*, p.* 
FROM orders o
LEFT JOIN order_item i ON o.id = i.order_id
LEFT JOIN product p ON i.product_id = p.id
WHERE o.id = ?

优化方案二:批量抓取策略

# application.properties
spring.jpa.properties.hibernate.default_batch_fetch_size=20
-- 优化后的查询:
SELECT * FROM order_item WHERE order_id IN (?, ?, ...) -- 一次查询20个订单项

优化方案三:JOIN FETCH查询

@Query("SELECT o FROM Order o " +
       "LEFT JOIN FETCH o.items i " +
       "LEFT JOIN FETCH i.product " +
       "WHERE o.createTime > :startDate")
List<Order> findRecentOrdersWithDetails(LocalDateTime startDate);

2.4.4 审计与版本控制(企业级数据管理)

自动审计字段配置

@EntityListeners(AuditingEntityListener.class)
@Entity
public class Product {
    // ...
    
    @CreatedBy
    private String createdBy;

    @LastModifiedBy
    private String modifiedBy;

    @Version
    private Long version;
}

// 配置审计信息获取
@Configuration
@EnableJpaAuditing
public class AuditConfig {
    @Bean
    public AuditorAware<String> auditorAware() {
        return () -> Optional.ofNullable(SecurityContextHolder.getContext())
                            .map(SecurityContext::getAuthentication)
                            .map(Authentication::getName);
    }
}

乐观锁控制案例

@Service
@RequiredArgsConstructor
public class InventoryService {
    private final ProductRepository productRepository;

    @Transactional
    public void reduceStock(Long productId, int quantity) {
        Product product = productRepository.findById(productId)
            .orElseThrow(() -> new ProductNotFoundException(productId));
        
        if (product.getStock() < quantity) {
            throw new InsufficientStockException();
        }
        
        product.setStock(product.getStock() - quantity);
        productRepository.save(product); // 自动检查@Version字段
    }
}

// 异常处理:
@ControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(OptimisticLockingFailureException.class)
    public ResponseEntity<?> handleOptimisticLocking() {
        return ResponseEntity.status(HttpStatus.CONFLICT)
            .body(Map.of("error", "数据版本冲突,请刷新后重试"));
    }
}

2.4.5 复杂查询解决方案(动态条件组合)

Criteria API动态查询

public class OrderSpecifications {
    public static Specification<Order> buildSearchSpec(
        String orderNumber, 
        LocalDate startDate, 
        LocalDate endDate,
        BigDecimal minAmount) {
        
        return (root, query, cb) -> {
            List<Predicate> predicates = new ArrayList<>();
            
            if (StringUtils.hasText(orderNumber)) {
                predicates.add(cb.like(root.get("orderNumber"), "%" + orderNumber + "%"));
            }
            
            if (startDate != null) {
                predicates.add(cb.greaterThanOrEqualTo(
                    root.get("createTime"), startDate.atStartOfDay()));
            }
            
            if (endDate != null) {
                predicates.add(cb.lessThanOrEqualTo(
                    root.get("createTime"), endDate.plusDays(1).atStartOfDay()));
            }
            
            if (minAmount != null) {
                predicates.add(cb.greaterThanOrEqualTo(
                    root.get("totalAmount"), minAmount));
            }
            
            return cb.and(predicates.toArray(new Predicate[0]));
        };
    }
}

// 业务层使用
public Page<Order> searchOrders(OrderSearchCriteria criteria, Pageable pageable) {
    Specification<Order> spec = OrderSpecifications.buildSearchSpec(
        criteria.getOrderNumber(),
        criteria.getStartDate(),
        criteria.getEndDate(),
        criteria.getMinAmount()
    );
    return orderRepository.findAll(spec, pageable);
}

QueryDSL集成方案

<!-- Maven依赖 -->
<dependency>
    <groupId>com.querydsl</groupId>
    <artifactId>querydsl-jpa</artifactId>
    <version>5.0.0</version>
</dependency>
// 自动生成Q类
@Generated("com.querydsl.codegen.DefaultEntitySerializer")
public class QOrder extends EntityPathBase<Order> {
    // 自动生成查询元模型
}

// 动态查询实现
public List<Order> findOrders(QOrderQuery query) {
    QOrder order = QOrder.order;
    JPAQuery<Order> jpaQuery = new JPAQuery<>(entityManager);
    
    return jpaQuery.select(order)
        .from(order)
        .where(order.status.eq(query.getStatus())
            .and(order.totalAmount.goe(query.getMinAmount()))
            .and(order.createTime.between(query.getStart(), query.getEnd())))
        .orderBy(order.createTime.desc())
        .fetch();
}

扩展说明总结

  1. JPA核心价值

    • 通过对象映射简化数据库操作
    • 提供标准化的持久层接口
    • 支持面向对象的查询语言(JPQL)
  2. 关联关系设计原则

    • 优先使用LAZY加载避免不必要查询
    • 明确维护方(mappedBy)
    • 谨慎使用级联操作
  3. 性能优化重点

    graph TD
        A[JPA性能优化] --> B[避免N+1查询]
        A --> C[合理使用二级缓存]
        A --> D[批量操作优化]
        B --> E["@EntityGraph"]
        B --> F[Batch Fetch]
        C --> G[Ehcache集成]
        D --> H[批量插入]
        D --> I[批量更新]
    
  4. 复杂查询选择策略

    • 简单查询:使用方法推导
    • 中等复杂度:使用@Query注解
    • 动态条件:使用Specification或QueryDSL 以下针对关键技术的深度扩展说明:

2.4.6 QueryDSL深度集成(企业级动态查询方案)

完整集成流程

1. Maven配置(含APT插件)

<dependencies>
    <!-- QueryDSL核心依赖 -->
    <dependency>
        <groupId>com.querydsl</groupId>
        <artifactId>querydsl-jpa</artifactId>
        <version>5.0.0</version>
    </dependency>
    
    <!-- 代码生成插件 -->
    <dependency>
        <groupId>com.querydsl</groupId>
        <artifactId>querydsl-apt</artifactId>
        <version>5.0.0</version>
        <scope>provided</scope>
    </dependency>
</dependencies>

<build>
    <plugins>
        <!-- APT处理器配置 -->
        <plugin>
            <groupId>com.mysema.maven</groupId>
            <artifactId>apt-maven-plugin</artifactId>
            <version>1.1.3</version>
            <executions>
                <execution>
                    <phase>generate-sources</phase>
                    <goals>
                        <goal>process</goal>
                    </goals>
                    <configuration>
                        <outputDirectory>target/generated-sources/querydsl</outputDirectory>
                        <processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

2. 自动生成的Q类示例

// QOrder.java(自动生成)
@Generated("com.querydsl.codegen.EntitySerializer")
public class QOrder extends EntityPathBase<Order> {

    private static final PathInits INITS = PathInits.DIRECT2;

    public static final QOrder order = new QOrder("order");

    public final NumberPath<Long> id = createNumber("id", Long.class);

    public final StringPath orderNumber = createString("orderNumber");

    public final ListPath<OrderItem, QOrderItem> items = 
        this.<OrderItem, QOrderItem>createList("items", OrderItem.class, QOrderItem.class, PathInits.DIRECT2);

    public QOrder(String variable) {
        super(Order.class, forVariable(variable));
    }
}

3. 复杂查询构建示例

@Repository
public class OrderCustomRepositoryImpl implements OrderCustomRepository {

    @PersistenceContext
    private EntityManager em;

    @Override
    public Page<Order> searchOrders(OrderSearchCondition condition, Pageable pageable) {
        QOrder order = QOrder.order;
        QOrderItem item = QOrderItem.orderItem;
        
        JPAQuery<Order> query = new JPAQueryFactory(em)
            .selectFrom(order)
            .leftJoin(order.items, item).fetchJoin()
            .where(
                order.status.eq(condition.getStatus())
                .and(order.createTime.between(
                    condition.getStartDate().atStartOfDay(),
                    condition.getEndDate().plusDays(1).atStartOfDay()
                ))
                .and(item.product.price.gt(condition.getMinPrice()))
            )
            .orderBy(order.createTime.desc())
            .groupBy(order.id);

        // 分页处理
        long total = query.fetchCount();
        List<Order> content = query
            .offset(pageable.getOffset())
            .limit(pageable.getPageSize())
            .fetch();

        return new PageImpl<>(content, pageable, total);
    }
}

4. 动态排序支持

private OrderSpecifier<?>[] createOrderSpecifiers(Pageable pageable) {
    return pageable.getSort().stream()
        .map(order -> {
            PathBuilder<Order> path = new PathBuilder<>(Order.class, "order");
            return new OrderSpecifier(
                order.isAscending() ? Order.ASC : Order.DESC,
                path.get(order.getProperty())
            );
        })
        .toArray(OrderSpecifier[]::new);
}

2.4.7 二级缓存配置(生产级优化方案)

Ehcache3集成全流程

1. 依赖配置

<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-jcache</artifactId>
</dependency>
<dependency>
    <groupId>org.ehcache</groupId>
    <artifactId>ehcache</artifactId>
    <version>3.10.8</version>
</dependency>

2. ehcache.xml配置

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="http://www.ehcache.org/ehcache.xsd">
    
    <persistence directory="/data/cache"/>
    
    <cache alias="productCache">
        <key-type>java.lang.Long</key-type>
        <value-type>com.example.Product</value-type>
        <expiry>
            <ttl unit="minutes">30</ttl>
        </expiry>
        <resources>
            <heap unit="MB">100</heap>
            <offheap unit="MB">200</offheap>
            <disk persistent="true" unit="GB">1</disk>
        </resources>
    </cache>
</config>

3. 实体类缓存注解

@Entity
@Cacheable
@org.hibernate.annotations.Cache(
    usage = CacheConcurrencyStrategy.READ_WRITE,
    region = "productCache"
)
public class Product {
    // ...
}

4. Spring Boot配置

# application.properties
spring.jpa.properties.hibernate.cache.use_second_level_cache=true
spring.jpa.properties.hibernate.cache.region.factory_class=jcache
spring.jpa.properties.hibernate.javax.cache.provider=org.ehcache.jsr107.EhcacheCachingProvider
spring.jpa.properties.hibernate.javax.cache.uri=classpath:ehcache.xml

5. 缓存监控方案

@Bean
public MeterRegistryCustomizer<MeterRegistry> cacheMetrics(CachingProvider provider) {
    return registry -> {
        CacheManager cacheManager = provider.getCacheManager();
        cacheManager.getCacheNames().forEach(name -> {
            Cache<?, ?> cache = cacheManager.getCache(name);
            registry.gauge("cache.size", Tags.of("name", name), cache::size);
        });
    };
}

2.4.8 乐观锁与审计增强实现

乐观锁深度控制
// 实体类版本控制
@Entity
public class Inventory {
    @Id
    private Long productId;
    
    @Version
    private Integer version;
    
    private Integer stock;
}

// 重试策略实现
@Retryable(value = OptimisticLockingFailureException.class, 
          maxAttempts = 3,
          backoff = @Backoff(delay = 100))
public void updateStockWithRetry(Long productId, int delta) {
    Inventory inv = inventoryRepo.findById(productId)
        .orElseThrow();
    inv.setStock(inv.getStock() + delta);
    inventoryRepo.save(inv);
}
审计功能扩展
// 自定义审计字段
@EntityListeners(AuditingEntityListener.class)
public class BaseEntity {
    @CreatedDate
    private LocalDateTime createdDate;
    
    @LastModifiedDate
    private LocalDateTime modifiedDate;
    
    @CreatedBy
    private String createdBy;
    
    @LastModifiedBy
    private String modifiedBy;
}

// 多租户审计实现
@Bean
public AuditorAware<TenantUser> auditorProvider() {
    return () -> Optional.ofNullable(SecurityContextHolder.getContext())
        .map(SecurityContext::getAuthentication)
        .filter(authentication -> authentication.getPrincipal() instanceof TenantUser)
        .map(authentication -> (TenantUser) authentication.getPrincipal());
}

2.4.9 复杂类型处理(JSON/XML字段映射)

JSON类型处理(Hibernate6+)

@Entity
public class ProductSpec {
    @Id
    private Long id;

    @JdbcTypeCode(SqlTypes.JSON)
    @Column(columnDefinition = "jsonb")
    private Map<String, Object> attributes = new HashMap<>();
}

// 自定义JSON转换器
@Converter(autoApply = true)
public class JsonConverter implements AttributeConverter<Map<String, Object>, String> {
    private static final ObjectMapper mapper = new ObjectMapper();

    @Override
    public String convertToDatabaseColumn(Map<String, Object> attribute) {
        try {
            return mapper.writeValueAsString(attribute);
        } catch (JsonProcessingException e) {
            throw new IllegalArgumentException("JSON转换失败", e);
        }
    }

    @Override
    public Map<String, Object> convertToEntityAttribute(String dbData) {
        try {
            return mapper.readValue(dbData, new TypeReference<>() {});
        } catch (IOException e) {
            throw new IllegalArgumentException("JSON解析失败", e);
        }
    }
}

扩展技术总结

  1. QueryDSL最佳实践

    graph LR
        A[动态查询需求] --> B{简单条件}
        B -->|是| C[方法命名推导]
        B -->|否| D{中等复杂度}
        D -->|是| E["@Query注解"]
        D -->|否| F{高度动态}
        F -->|是| G[QueryDSL]
        F -->|否| H[Criteria API]
    
  2. 缓存策略选择

    策略类型适用场景注意事项
    READ_ONLY只读数据(字典表等)不支持更新操作
    NONSTRICT_READ_WRITE偶尔更新的数据可能短暂数据不一致
    READ_WRITE高频读写数据需要事务支持
    TRANSACTIONAL分布式事务环境性能开销较大
  3. 乐观锁实现要点

    • 使用@Version字段控制版本
    • 结合重试机制处理并发冲突
    • 前端需处理HTTP 409 Conflict响应
  4. 复杂类型存储方案

    • JSON类型:适合非结构化数据
    • XML类型:适合严格模式数据
    • 二进制类型:适合文件存储

2.5 分布式事务方案(Saga模式深度实践)

2.5.1 分布式事务核心挑战

典型问题场景

电商系统下单流程:
1. 订单服务 → 创建订单
2. 库存服务 → 扣减库存
3. 物流服务 → 生成物流单

异常场景:
- 订单创建成功但库存不足
- 库存扣减后物流服务不可用
- 网络分区导致部分服务成功

事务模式对比

模式一致性模型实现复杂度适用场景
2PC强一致性数据库层跨库事务
TCC最终一致性高一致性要求的金融交易
Saga最终一致性长事务、跨服务操作
本地消息表最终一致性异步通知型业务

2.5.2 Saga模式实现原理

模式架构图
sequenceDiagram
    participant C as 协调器
    participant O as 订单服务
    participant I as 库存服务
    participant L as 物流服务
    
    C->>O: 1. 创建订单
    O-->>C: 订单创建成功
    C->>I: 2. 扣减库存
    I-->>C: 库存扣减成功
    C->>L: 3. 创建物流单
    L-->>C: 物流单创建失败
    C->>I: 4. 补偿库存
    I-->>C: 库存恢复成功
    C->>O: 5. 取消订单
实现方式对比
类型控制方式优点缺点
编排式(Choreography)事件驱动去中心化、服务自治调试困难、易现循环依赖
编排式(Orchestration)中央协调器流程可视化、易管理单点风险、耦合协调逻辑

2.5.3 订单-库存-物流Saga实现(编排式)

项目结构
saga-demo/
├── order-service/
├── inventory-service/
├── logistics-service/
└── saga-coordinator/
协调器核心实现
// Saga协调器配置
@Configuration
public class SagaConfig {
    
    @Bean
    public SagaCoordinator sagaCoordinator(
        OrderService orderService,
        InventoryService inventoryService,
        LogisticsService logisticsService) {
        
        return SagaBuilder
            .begin("创建订单", orderService::createOrder)
            .then("扣减库存", inventoryService::deductStock)
            .then("生成物流单", logisticsService::createLogistics)
            .withCompensation("订单取消", orderService::cancelOrder)
            .withCompensation("库存恢复", inventoryService::restoreStock)
            .build();
    }
}

// Saga执行器
public class SagaExecutor {
    private final List<SagaStep> steps;
    private final List<BiConsumer<SagaContext, Exception>> compensations = new ArrayList<>();

    public void execute(SagaContext context) {
        try {
            for (SagaStep step : steps) {
                step.execute(context);
            }
        } catch (Exception e) {
            executeCompensation(context);
            throw new SagaException("Saga执行失败", e);
        }
    }

    private void executeCompensation(SagaContext context) {
        Collections.reverse(compensations);
        compensations.forEach(comp -> comp.accept(context, null));
    }
}
订单服务实现
@Service
public class OrderService {
    private static final Map<Long, Order> orders = new ConcurrentHashMap<>();

    @SagaParticipant
    public void createOrder(SagaContext context) {
        Order order = new Order(
            context.get("userId"), 
            context.get("productId"),
            context.get("quantity")
        );
        orders.put(order.getId(), order);
        context.put("orderId", order.getId());
    }

    @Compensation
    public void cancelOrder(SagaContext context) {
        Long orderId = context.get("orderId");
        orders.get(orderId).setStatus(OrderStatus.CANCELLED);
    }
}

2.5.4 异常处理与恢复机制

重试策略配置

@Bean
public RetryTemplate sagaRetryTemplate() {
    return new RetryTemplateBuilder()
        .maxAttempts(3)
        .exponentialBackoff(1000, 2, 5000)
        .retryOn(SagaRetryableException.class)
        .build();
}

// 服务层应用
@SagaParticipant
@Retryable(retryFor = InventoryServiceException.class, 
          maxAttempts = 3,
          backoff = @Backoff(delay = 1000))
public void deductStock(SagaContext context) {
    // 库存扣减逻辑
    if (currentStock < required) {
        throw new InventoryServiceException("库存不足");
    }
    // ...
}

事务日志记录

@Entity
public class SagaLog {
    @Id
    private String sagaId;
    
    private SagaStatus status;
    
    @Lob
    private String contextJson;
    
    @ElementCollection
    private List<String> executedSteps;
    
    @ElementCollection
    private List<String> compensatedSteps;
}

// 日志切面
@Aspect
@Component
public class SagaLogAspect {
    @Autowired
    private SagaLogRepository logRepository;

    @Around("@annotation(SagaParticipant)")
    public Object logStep(ProceedingJoinPoint pjp) throws Throwable {
        String stepName = ((MethodSignature)pjp.getSignature()).getMethod().getName();
        SagaContext context = (SagaContext) pjp.getArgs()[0];
        
        SagaLog log = logRepository.findBySagaId(context.getSagaId())
            .orElseGet(() -> new SagaLog(context.getSagaId()));
        
        try {
            Object result = pjp.proceed();
            log.addExecutedStep(stepName);
            logRepository.save(log);
            return result;
        } catch (Exception e) {
            log.addCompensatedStep(stepName);
            log.setStatus(SagaStatus.FAILED);
            logRepository.save(log);
            throw e;
        }
    }
}

2.5.5 生产环境部署方案

Kubernetes部署配置

# saga-coordinator-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: saga-coordinator
spec:
  replicas: 3
  strategy:
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  template:
    spec:
      containers:
        - name: coordinator
          image: registry.example.com/saga-coordinator:1.0.0
          env:
            - name: SPRING_DATASOURCE_URL
              value: jdbc:mysql://mysql-cluster:3306/saga
            - name: SPRING_REDIS_HOST
              value: redis-sentinel

# 服务熔断配置
spring:
  cloud:
    circuitbreaker:
      resilience4j:
        instances:
          saga:
            failureRateThreshold: 50
            waitDurationInOpenState: 10s
            slidingWindowSize: 10

监控指标暴露

@Bean
public MeterRegistryCustomizer<MeterRegistry> sagaMetrics() {
    return registry -> {
        Gauge.builder("saga.active_count", 
                     SagaCoordinator::getActiveCount)
            .register(registry);
        
        Timer.builder("saga.duration")
            .publishPercentiles(0.5, 0.95)
            .register(registry);
    };
}

总结与最佳实践

Saga模式适用场景

graph TD
    A[是否长周期事务?] -->|是| B[考虑Saga]
    A -->|否| C[考虑本地事务]
    B --> D{是否需要强一致性?}
    D -->|是| E[结合TCC模式]
    D -->|否| F[纯Saga实现]

实施要点

  1. 服务自治:每个参与者服务需独立管理本地事务
  2. 幂等设计:所有操作必须支持重试
  3. 补偿事务:确保每个正向操作都有对应的补偿逻辑
  4. 可视化监控:记录完整事务链路
  5. 压力测试:验证分布式锁和重试机制的性能

容错模式推荐

故障类型处理策略
业务校验失败立即中断并触发补偿
网络临时故障指数退避重试
服务不可用熔断降级+人工干预
数据不一致定时对账修复