1. 分库分表路由组件的核心设计思路
在大型互联网应用中,随着业务数据量的快速增长,单库单表的性能瓶颈日益凸显。以电商系统为例,一个日订单量百万级的平台,如果所有订单数据都存储在单一数据库表中,不仅查询效率低下,还会带来严重的锁竞争问题。这时候,分库分表就成了必选项。
1.1 为什么需要路由组件
传统分库分表方案最直接的实现方式,就是在业务代码中硬编码分片逻辑。比如:
java复制// 硬编码的分库分表示例(不推荐)
public void createOrder(Order order) {
int userId = order.getUserId();
int dbIndex = userId % 2; // 2个库
int tableIndex = userId % 6; // 6个表
// 根据不同索引选择不同数据源
DataSource dataSource = getDataSource(dbIndex);
// 拼接表名
String tableName = "order_" + tableIndex;
// 执行SQL
jdbcTemplate.update("INSERT INTO " + tableName + "...", ...);
}
这种实现方式存在三个致命问题:
- 代码侵入性强:业务逻辑与分片逻辑高度耦合
- 维护成本高:增减分库分表数量时需要修改大量业务代码
- 容易出错:开发人员需要手动处理各种分片场景
1.2 AOP+ThreadLocal的优雅解决方案
我们采用面向切面编程(AOP)结合ThreadLocal的方案,可以实现分库分表逻辑与业务代码的完全解耦。核心设计思想是:
- 拦截阶段:通过AOP拦截所有需要分片的DAO方法
- 计算阶段:从方法参数中提取分片键(如userId),计算分库分表位置
- 传递阶段:将计算结果存入ThreadLocal
- 执行阶段:动态数据源和MyBatis插件从ThreadLocal获取分片信息
这种架构下,业务代码完全不需要感知分库分表的存在:
java复制// 优化后的业务代码(完全无分库分表痕迹)
public void createOrder(Order order) {
// 无需关心数据存储细节
orderMapper.insert(order);
}
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. AOP切面的详细实现
2.1 切面类定义与配置
首先创建一个分片切面类,使用Spring AOP的@Aspect注解:
java复制@Aspect
@Component
public class ShardingAspect {
private static final Logger logger = LoggerFactory.getLogger(ShardingAspect.class);
// 定义切入点:拦截所有Mapper接口的方法
@Pointcut("execution(* com.example.mapper.*.*(..))")
public void shardingPointcut() {}
// 环绕通知
@Around("shardingPointcut()")
public Object doSharding(ProceedingJoinPoint joinPoint) throws Throwable {
try {
// 1. 获取方法参数
Object[] args = joinPoint.getArgs();
// 2. 提取分片键(这里以userId为例)
Long userId = extractUserId(args);
// 3. 计算分库分表路由
calculateRoute(userId);
// 4. 执行原方法
return joinPoint.proceed();
} finally {
// 5. 清除ThreadLocal数据
ShardingContext.clear();
}
}
// 其他辅助方法...
}
2.2 分片键提取策略
在实际业务中,分片键的提取需要根据业务场景灵活处理。以下是几种常见情况的处理方式:
java复制private Long extractUserId(Object[] args) {
for (Object arg : args) {
// 情况1:参数本身就是分片键
if (arg instanceof Long) {
return (Long) arg;
}
// 情况2:参数对象中包含分片键字段
if (arg instanceof UserIdHolder) {
return ((UserIdHolder) arg).getUserId();
}
// 情况3:使用注解标记分片键参数
