多数据源 + ShardingSphere 分库分表 + 读写分离 实现方案

概述

本项目实现了基于 dynamic-datasource + ShardingSphere-JDBC 的多数据源方案,每个数据源可独立配置分库分表和读写分离策略,并通过请求头拦截器实现动态数据源切换。

核心架构

复制代码
请求 → UserHeaderInterceptor(拦截器) → DataSourceRouter(路由) 
     → DynamicRoutingDataSource(动态数据源) → ShardingSphere(分库分表/读写分离)

主要实现步骤

1. 配置 ShardingSphere 数据源

ShardingJdbcConfig.java - 创建测试数据源

java 复制代码
@Configuration
public class ShardingJdbcConfig {

    @Value("${sharding.jdbc.config.file}")
    private Resource shardingConfigFileTest;

    @Bean
    public DataSource dataSourceTest() throws SQLException, IOException {
        return YamlShardingDataSourceFactory.createDataSource(shardingConfigFileTest.getFile());
    }
}

通过 YAML 配置文件 sharding-jdbc-test.yaml 创建独立的 ShardingSphere 数据源,支持:

  • 分库分表策略
  • 读写分离配置

2. 整合动态数据源

MyDataSourceConfiguration.java - 将 ShardingSphere 数据源注册到动态数据源

java 复制代码
@Configuration
@AutoConfigureBefore({DynamicDataSourceAutoConfiguration.class, SpringBootConfiguration.class})
public class MyDataSourceConfiguration {

    @Lazy
    @Resource
    private DataSource shardingDataSource;  // 主数据源

    @Lazy
    @Resource
    private DataSource dataSourceTest;      // 测试数据源

    @Bean
    public DynamicDataSourceProvider dynamicDataSourceProvider() {
        return new AbstractDataSourceProvider(dataSourceCreator) {
            @Override
            public Map<String, DataSource> loadDataSources() {
                Map<String, DataSource> dataSourceMap = createDataSourceMap(dataSourcePropertyMap);
                dataSourceMap.put("sharding", shardingDataSource);  // 注册主数据源
                dataSourceMap.put("test", dataSourceTest);          // 注册测试数据源
                return dataSourceMap;
            }
        };
    }
}

3. 请求拦截器实现数据源切换

UserHeaderInterceptor.java - 根据请求头动态切换数据源

java 复制代码
@Slf4j
@RequiredArgsConstructor
public class UserHeaderInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        String headerValue = request.getHeader("isTestUser");
        
        if (StrUtil.isNotBlank(headerValue) && headerValue.equals("true")) {
            log.info("request uri:{}, isTestUser:{}", request.getRequestURI(), headerValue);
            DataSourceRouter.setTestUser(true);  // 切换到测试数据源
        }
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, 
                                Object handler, Exception ex) {
        DataSourceRouter.clearTestUser();  // 清理数据源上下文
    }
}

4. 数据源路由工具

DataSourceRouter.java - 使用 ThreadLocal 存储数据源标识

java 复制代码
@Component
public class DataSourceRouter {
    private static final ThreadLocal<Boolean> isTestUser = new ThreadLocal<>();

    public static void setTestUser(boolean isTest) {
        isTestUser.set(isTest);
        DynamicRoutingDataSourceService bean = SpringUtils.getBean(DynamicRoutingDataSourceService.class);
        bean.push(isTest);  // 切换数据源
    }

    public static void clearTestUser() {
        isTestUser.remove();
        DynamicRoutingDataSourceService bean = SpringUtils.getBean(DynamicRoutingDataSourceService.class);
        bean.clear();  // 清理数据源上下文
    }
}

5. 动态数据源切换服务

DynamicRoutingDataSourceServiceImpl.java - 调用 dynamic-datasource 的 API 切换数据源

java 复制代码
@Service
public class DynamicRoutingDataSourceServiceImpl implements DynamicRoutingDataSourceService {

    @Override
    public void push(Boolean isTestUser) {
        String dsKey = isTestUser ? "test" : "sharding";
        DynamicDataSourceContextHolder.push(dsKey);  // 切换到指定数据源
    }

    @Override
    public void clear() {
        DynamicDataSourceContextHolder.clear();  // 清理上下文
    }
}

6. 注册拦截器

WebConfig.java

java 复制代码
@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new UserHeaderInterceptor());
    }
}

配置文件说明

application.yml - 主数据源配置

yaml 复制代码
spring:
  datasource:
    dynamic:
      primary: sharding  # 默认使用 sharding 数据源
      
  shardingsphere:
    datasource:
      names: ds-master,ds-slave,ds-master-0,ds-master-1
      # 配置各数据源连接信息...
    sharding:
      # 分库分表策略...
      master-slave-rules:
        # 读写分离配置...

sharding-jdbc-test.yaml - 测试数据源配置

独立的 ShardingSphere 配置文件,包含测试环境的分库分表和读写分离规则。

使用方式

请求时添加 Header:

复制代码
isTestUser: true   → 使用测试数据源 (test)
isTestUser: false  → 使用主数据源 (sharding)
不传递该 Header   → 使用主数据源 (sharding)

技术栈

组件 版本
Spring Boot 2.7.14
ShardingSphere-JDBC 4.1.1
dynamic-datasource-spring-boot-starter 4.1.3
MyBatis-Plus 3.4.0
MySQL Connector 8.0.28

流程图

复制代码
┌─────────────────┐
│   HTTP 请求      │
│ Header: isTestUser │
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│ UserHeaderInterceptor │
│   解析请求头      │
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│  DataSourceRouter │
│  设置 ThreadLocal │
└────────┬────────┘
         │
         ▼
┌─────────────────────────┐
│ DynamicDataSourceContextHolder │
│      切换数据源上下文      │
└────────┬────────────────┘
         │
    ┌────┴────┐
    │         │
    ▼         ▼
┌───────┐  ┌───────┐
│sharding│  │ test  │
│ 主数据源 │  │测试数据源│
└───┬───┘  └───┬───┘
    │         │
    ▼         ▼
┌─────────────────┐
│  ShardingSphere  │
│ 分库分表/读写分离 │
└─────────────────┘

代码示例

https://github.com/lolkt/dynamic-shardingsphere

相关推荐
熊文豪8 小时前
【金仓数据库征文】不装中间件的 MySQL→金仓在线迁移,mysql_fdw 全流程,和一个差点漏掉的 emoji
数据库·mysql·中间件·电科金仓
拆房老料2 天前
ONLYOFFICE AI Agent 深度解析:在线 Office 正从编辑器走向智能工作平台
人工智能·中间件·编辑器·word·开源软件
知行合一。。。2 天前
LangChain--08--中间件(Middleware)
中间件·langchain
●VON2 天前
鸿蒙 PC Markdown 编辑器可靠性设计:未保存内容的崩溃恢复闭环
华为·中间件·编辑器·harmonyos·鸿蒙
fuquxiaoguang4 天前
中间件的“价值重估”:传统同质化竞争终结,AI智能编排时代开启
人工智能·中间件
fuquxiaoguang5 天前
中间件的“结构性升级”:当智能编排成为政企采购的“硬指标”
中间件·ai智能体·ai原生
C137的本贾尼5 天前
第七篇:消息队列(MQ)——就是个带存储的异步通信管道
java·开发语言·中间件
半夜修仙5 天前
RabbitMQ的推模式和拉模式
java·分布式·中间件·rabbitmq·github·java-rabbitmq
Sagittarius_A*6 天前
Web 渗透实战:服务器系统识别、端口与中间件全量探测
服务器·网络安全·中间件·渗透测试
猿与禅7 天前
CosId 分布式 ID 生成器完全教程:从架构原理到生产落地
java·shardingsphere·雪花算法·分布式id·高性能·cosid·号段模式