题目
高并发场景下如何设计Spring MVC接口保证数据强一致性?
信息
- 类型:问答
- 难度:⭐⭐⭐
考点
分布式事务处理,高并发优化策略,缓存与数据库一致性,Spring MVC深度优化,限流熔断机制
快速回答
实现要点:
- 采用TCC分布式事务或Saga模式替代传统@Transactional
- 使用Redis+Lua脚本实现原子化缓存操作
- 通过分库分表+读写分离优化数据库访问
- 应用限流熔断(Sentinel/Resilience4j)保护系统
- 使用异步处理(@Async+消息队列)解耦核心逻辑
核心挑战与解决思路
在10万QPS场景下,传统Spring MVC架构面临:1)数据库连接池耗尽 2)事务冲突 3)缓存不一致 4)服务雪崩。需采用分布式架构+最终一致性方案。
关键实现方案
1. 分布式事务处理(代码示例)
// TCC模式实现示例
@Service
public class PaymentService {
@Transactional
public void tryPhase(@RequestParam Long orderId,
@RequestParam BigDecimal amount) {
// 1. 冻结资金(Try)
accountClient.freeze(orderId, amount);
// 2. 创建本地事务记录
txLogRepo.save(new TransactionLog(orderId, "PAYMENT"));
}
@Transactional
public void confirmPhase(Long orderId) {
// 1. 实际扣款(Confirm)
accountClient.deduct(orderId);
// 2. 更新事务状态
txLogRepo.updateStatus(orderId, "CONFIRMED");
}
// 补偿逻辑(Cancel)类似
}对比方案:
| 方案 | 适用场景 | 缺点 |
|---|---|---|
| 2PC | 强一致性要求 | 性能瓶颈 |
| TCC | 金融交易 | 开发复杂 |
| Saga | 长事务 | 补偿逻辑难 |
2. 缓存一致性设计
// Redis+Lua保证原子性
String luaScript = "if redis.call('get', KEYS[1]) == ARGV[1] " +
"then redis.call('del', KEYS[1]) " +
"return 1 else return 0 end";
redisTemplate.execute(new DefaultRedisScript<>(luaScript, Long.class),
Collections.singletonList("stock_lock"), orderId);缓存策略对比:
- Cache-Aside: 先更DB再删缓存,需延迟双删
- Write-Behind: 写缓存异步批量刷DB
- Read-Through: 缓存失效时由缓存服务加载
3. 高并发优化措施
- 数据库层:
- 分库分表(ShardingSphere)
- 读写分离 + 连接池优化(HikariCP配置maxPoolSize=500)
- 服务层:
- 线程池隔离:不同业务使用独立线程池
- 异步化:@Async处理非核心逻辑
- Web层:
- Tomcat参数调优:maxThreads=1000, acceptCount=2000
- 禁用ViewResolver(返回JSON场景)
常见错误与规避
- 错误1: 在Controller内直接加@Transactional
规避: 事务应在Service层,且超时时间<3s - 错误2: 循环依赖导致事务失效
规避: 使用@Lazy或Setter注入 - 错误3: 缓存未设置过期时间导致内存泄漏
规避: 设置TTL并启用淘汰策略
扩展知识
- 流量控制: Sentinel集群限流(而非单机限流)
- 监控体系: Micrometer+Prometheus监控JVM/DB连接池
- 热点数据处理: 本地缓存+Redis分片
- 零信任设计: 对所有入参进行Sharding Key验证