十八、微服务之ShardingSphere(包含多数据源)

十八、微服务之ShardingSphere(包含多数据源)

ShardingSphere 介绍与准备

ShardingSphere 是 Apache 提供的一个分表分库中间件,需要在项目中集成分表功能又或者分库功能可以使用,旧项目使用也无需修改原 SQL,兼容其他 orm 框架;此文章只记录分表功能实现,分库是为了数据库层分流减少数据库的压力,个人建议读写分离使用其他中间件(譬如:MyCat),考虑业务场景,分表是为了解决数据量过大导致查询效率慢的问题,我们可以通过优化分表的规则解决这个问题,另外如果打算在业务层控制读写分离,开发成本会提高并且需要开发人员对业务以及代码编写非常熟悉,能够准确在指定位置切换数据源,操作繁琐,个人不考虑这种方式的读写分离实现方式;当然,本文也会额外介绍多数据源的配置。

依赖
<dependency>
    <groupId>org.apache.shardingsphere</groupId>
    <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
    <version>4.1.1</version>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.22</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.2.9.RELEASE</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.21</version>
</dependency>
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.4.2</version>
</dependency>
配置文件参数
spring.datasource.ds0.url=jdbc:mysql://ds0 
spring.datasource.ds0.username=*** 
spring.datasource.ds0.password=*** 
spring.datasource.ds0.driver-class-name=com.mysql.cj.jdbc.Driver 
spring.datasource.ds1.url=jdbc:mysql://ds1 spring.datasource.ds1.username=*** 
spring.datasource.ds1.password=*** 
spring.datasource.ds1.driver-class-name=com.mysql.cj.jdbc.Driver 
spring.datasource.ds2.url=jdbc:mysql://ds2 
spring.datasource.ds2.username=*** spring.datasource.ds2.password=*** 
spring.datasource.ds2.driver-class-name=com.mysql.cj.jdbc.Driver
配置类
/** * 举例配置主数据源,其他次数据源类似,主数据源建议加上 @Primary */

import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import org.apache.shardingsphere.transaction.core.TransactionType;
import org.apache.shardingsphere.transaction.core.TransactionTypeHolder;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;
import java.sql.SQLException;

@Configuration
@MapperScan(basePackages = "数据库接口层目录", sqlSessionFactoryRef = "mainSqlSessionFactory")
public class OpenDataSourceConfig {
    @Autowired
    private MybatisPlusInterceptor mybatisPlusInterceptor;

    /** * 使用 DruidDataSourceBuilder 创建数据源即可引入对应阿里的数据库连接池 */
    @Bean("mainDateSource")
    @ConfigurationProperties(prefix = "spring.datasource.ds0")
    public DataSource mainDateSource() {
        return DruidDataSourceBuilder.create().build();
    }

    /** * 分表配置 */
    @Bean(name = "mainShardingDataSource")
    public DataSource mainShardingDataSource(@Qualifier("mainDateSource") DataSource mainDateSource) throws SQLException {
        TransactionTypeHolder.set(TransactionType.LOCAL);
        // 1、指定需要分表的数据源 
        Map<String, DataSource> dataSourceMap = new HashMap<>();
        dataSourceMap.put("main", mainDateSource);
        // 2、分表配置
        ShardingRuleConfiguration shardingRuleConfig = new ShardingRuleConfiguration();
        shardingRuleConfig.getTableRuleConfigs().addAll(CollUtil.newArrayList(pageRecordConfig()));
        shardingRuleConfig.setDefaultDataSourceName("main");
        return ShardingDataSourceFactory.createDataSource(dataSourceMap, shardingRuleConfig, new Properties());
    }

    /** * 会话工厂 */
    @Bean(name = "mainSqlSessionFactory")
    public SqlSessionFactory mainSqlSessionFactory(@Qualifier("mainShardingDataSource") DataSource dataSource) throws Exception {
        MybatisSqlSessionFactoryBean sessionFactory = new MybatisSqlSessionFactoryBean();
        sessionFactory.setDataSource(dataSource);
        sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:静态SQL文件的目录"));
        MybatisConfiguration configuration = new MybatisConfiguration();
        configuration.setJdbcTypeForNull(JdbcType.NULL);
        configuration.setMapUnderscoreToCamelCase(true);
        configuration.setCacheEnabled(false);
        sessionFactory.setConfiguration(configuration);
        sessionFactory.setPlugins(mybatisPlusInterceptor);
        return sessionFactory.getObject();
    }

    /** * 事务管理器 */
    @Bean(name = "mainTransactionManager")
    public DataSourceTransactionManager mainTransactionManager(@Qualifier("mainShardingDataSource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

    /** * 分表策略设置 * * @return */
    TableRuleConfiguration pageRecordConfig() {
        TableRuleConfiguration result = new TableRuleConfiguration("page_record", "main.page_record_${0..15}");
        // 配置分表策略,缺省表示使用默认分表策略 
        result.setTableShardingStrategyConfig(new InlineShardingStrategyConfiguration("id", "page_record_${id % 16}"));
        return result;
    }
}
注意事项:
  1. Mysql 使用的版本为 8,因此驱动使用 com.mysql.cj.jdbc.Driver;
  2. 引入第三方连接池需要指定才会生效,本文中使用阿里连接池,因为数据库地址属性配置名称为 spring.datasource.url,默认连接池的地址属性配置名称为 spring.datasource.jdbc;
  3. 多数据源情况下需要指定主数据源以及对应的事物管理器。