题目
设计支持多数据源动态切换和事务管理的数据库访问框架
信息
- 类型:问答
- 难度:⭐⭐⭐
考点
抽象工厂模式,策略模式,事务管理,动态代理,连接池管理
快速回答
核心实现要点:
- 使用抽象工厂模式创建不同数据源的数据库连接
- 通过策略模式实现运行时数据源切换逻辑
- 结合ThreadLocal保证线程级数据源隔离
- 采用Spring事务管理扩展点实现事务同步
- 利用动态代理实现方法级数据源路由
- 通过连接池包装器防止连接泄漏
问题背景
在大型分布式系统中,需要根据业务场景动态切换主/从数据库、分库分表或不同厂商数据库。难点在于:1)运行时无缝切换 2)事务一致性 3)资源安全管理。
核心设计
1. 模式组合架构
// 抽象工厂模式 - 数据源创建
public abstract class DataSourceFactory {
public abstract DataSource createDataSource(Config config);
}
// 策略模式 - 数据源选择
public interface RoutingStrategy {
String determineDataSource(Method method, Object[] args);
}2. 动态数据源路由
// 动态代理实现路由
public class DataSourceProxy implements InvocationHandler {
private Object target;
private RoutingStrategy strategy;
public Object invoke(Object proxy, Method method, Object[] args) {
String dsKey = strategy.determineDataSource(method, args);
DataSourceContextHolder.set(dsKey); // 使用ThreadLocal绑定
try {
return method.invoke(target, args);
} finally {
DataSourceContextHolder.clear();
}
}
}3. 事务同步关键代码
// 继承AbstractPlatformTransactionManager
public class MultiDataSourceTransactionManager extends AbstractPlatformTransactionManager {
protected Object doGetTransaction() {
// 获取当前线程绑定的真实连接
Connection conn = DataSourceContextHolder.getConnection();
return new ConnectionHolder(conn);
}
protected void doBegin(Object transaction, TransactionDefinition definition) {
ConnectionHolder conHolder = (ConnectionHolder) transaction;
conHolder.setAutoCommit(false); // 开启事务
}
}最佳实践
- 连接泄漏防护:包装Connection,在finally块强制归还连接池
- 事务传播机制:使用Spring的TransactionSynchronizationManager管理嵌套事务
- 降级策略:主库不可用时自动切换到备库并记录告警
- 性能优化:为只读操作指定从库,通过AOP注解实现:
@DataSource(name="slave", readOnly=true)
常见错误
| 错误类型 | 后果 | 解决方案 |
|---|---|---|
| 未清除ThreadLocal | 后续请求数据源污染 | try-finally中强制清理 |
| 跨数据源事务 | 部分提交部分回滚 | 使用Seata等分布式事务框架 |
| 连接未关闭 | 连接池耗尽 | 代理Connection.close()自动归还 |
扩展知识
- XA事务处理:通过JTA实现多资源管理器协调
- 健康检查机制:定时PING数据库检测存活状态
- 负载均衡策略:基于权重的从库选择算法
- Spring Boot集成:通过AbstractRoutingDataSource简化实现