Spring整合sharding-jdbc使用踩坑

前言

项目中需要对数据库进行分库分表操作,所以使用sharding-jdbc来实现此功能

引入依赖

<dependency>
    <groupId>org.apache.shardingsphere</groupId>
    <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
    <version>4.0.0-RC1</version>
</dependency>

简单原理分析

  1. org.apache.shardingsphere.shardingjdbc.spring.boot.SpringBootConfiguration 导入自动配置类,根据配置文件中配置的信息创建 DateSource 列表
  2. 创建 ShardingDataSource 替代之前的 DateSource 列表
  3. 获取连接的实现类为 ShardingConnection

踩坑1

背景

项目中需要配置两套数据源,一套走分表,一套不走。

错误

会报错找不到事务执行器TransactionTemplate的Bean,本质上还是找不到TransactionManager

解决方式

通过手动配置这两个Bean解决了,根本原因为项目代码中自定义了一个DataSource,sharding-jdbc自动配置中也定义了一个通用DataSource但是没有设置为Primary,导致JdbcTransactionManagerConfiguration不能自动加载。

通过自定义BeanDefinitionRegistryPostProcessor也不能解决,因为它会在ConfigurationClassPostProcessor之后执行,如果想让它在ConfigurationClassPostProcessor之前执行,它又找不到DataSource这个BeanDefinition,所以这种方法行不通,只能手动配置TransactionManager了。

踩坑2

查询条件如果不包含分片列或者操作符不是(=,in,between),不会走分片算法

踩坑3

分库分表之后,更新或删除或查询操作的条件也必须带上分片键,不然会扫描所有库和表。

具体可以看org.apache.shardingsphere.core.route.type.standard.StandardRoutingEngine#getDataNodes方法

通过 spring.shardingsphere.props.sql.show=true 可以看到最终执行的SQL

踩坑4

背景

spring.shardingsphere.datasource.names=ds0_master,ds0_slave0

# 配置第1库的主从
spring.shardingsphere.datasource.ds0_master.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.ds0_master.driver-class-name=com.mysql.jdbc.Driver

在配置分库分表时遇到报错
Reason: Canonical names should be kebab-case ('-' separated), lowercase alpha-numeric characters and must start with a letter

原因

前缀不能包含下划线,spring.shardingsphere.datasource.${databaseName}

模拟报错

public static void main(String[] args) {
        Map<String, Object> map = new HashMap<>();
        map.put("bo_go.private_key.apikey", "abc");
        MapPropertySource propertySource = new MapPropertySource("test", map);
        StandardEnvironment environment = new StandardEnvironment();
        environment.getPropertySources().addFirst(propertySource);
        Binder binder = Binder.get(environment);
        BindResult<BogoConfig> bindResult = binder.bind("bo_go", BogoConfig.class);
        System.out.println(bindResult);
    }

解决方式

使用中划线 ds0-master

spring.shardingsphere.datasource.names=ds0-master,ds0-slave0

# 配置第1库的主从
spring.shardingsphere.datasource.ds0-master.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.ds0-master.driver-class-name=com.mysql.jdbc.Driver

踩坑5

第一次请求数据库很慢,要2-3秒(第二次仅需200ms),通过arthas分析,发现主要耗时在 创建 SQL解析器

profiler start -- 开启采样
profiler stop --format flamegraph -- 结束采样并生成火焰图

优化方案如下,优化前 2.4S,优化后1.2S,会提前加载 OptimizeEngineFactory 和 RoutingEngineFactory

@Slf4j
@Configuration
@ConditionalOnProperty(value = "shardingsphere.preload.enabled", havingValue = "true", matchIfMissing = true)
public class ShardingPreLoadConfig implements InitializingBean {

    @Autowired
    private MarketingBatchOrderMapper batchOrderMapper;

    @Override
    public void afterPropertiesSet() throws Exception {
        String batchOrderId = "xxx";
        // 触发 SQL 解析引擎预热
        batchOrderMapper.selectByPrimaryKey(batchOrderId);
        log.info("sharding-jdbc SQL解析引擎预热");
    }
}

分片算法优化

分库分表分片算法优化: 分库分表联合取模

原理:将分库和分表视为一个整体分片(总片数=4×16=64),再分解为库和表索引。
优势:确保所有库表组合均被覆盖,避免空洞

int totalShards = 64;  // 4库 × 16表
int shardIndex = Math.abs(hash) % totalShards;
int dbIndex = shardIndex / 16;   // 分库索引
int tableIndex = shardIndex % 16; // 分表索引

参考

JVM CPU Profiler技术原理及源码深度解析

posted @ 2026-04-11 14:36  strongmore  阅读(0)  评论(0)    收藏  举报