ShardingSphere + MySQL implementation sub database
1, About ShardingSphere
ShardingSphere is an ecosystem composed of a set of open-source distributed database middleware solutions. It is composed of sharding JDBC, sharding proxy and sharding sidecar (planned). They all provide standardized data fragmentation, distributed transaction and database governance functions, which can be applied to various application scenarios such as Java isomorphism, heterogeneous language, cloud native and so on.
- Sharding JDBC is positioned as a lightweight Java framework and provides additional services in the JDBC layer of Java. It uses the client to directly connect to the database and provides services in the form of jar package without additional deployment and dependency. It can be understood as an enhanced jdbc driver and is fully compatible with JDBC and various ORM frameworks.
- Sharding proxy is positioned as a transparent database proxy. It provides a server version that encapsulates the database binary protocol to support heterogeneous languages. At present, the MySQL/PostgreSQL version is provided first. It can use any access client compatible with MySQL/PostgreSQL protocol (such as MySQL Command Client, MySQL Workbench, Navicat, etc.) to operate data, which is more friendly to DBA.
Architecture diagram
2, Project configuration
Intention introduction: two databases: ds0 and ds1. Each database has two tables: order and order_item, just according to order_id sub database.
1. POM introduces ShardingSphere database and table dependency
<shardingsphere.version>4.1.1</shardingsphere.version> <dependency> <groupId>org.apache.shardingsphere</groupId> <artifactId>sharding-jdbc-spring-boot-starter</artifactId> <version>${shardingsphere.version}</version> </dependency> <dependency> <groupId>org.apache.shardingsphere</groupId> <artifactId>sharding-jdbc-spring-namespace</artifactId> <version>${shardingsphere.version}</version> </dependency>
2. YAML configuration
Sharding JDBC can be configured through Java, YAML and Spring namespace. For details, please refer to the configuration manual on the official website of ShardingSphere ShardingSphere official website configuration manual , with detailed configuration instructions.
Since only sub databases are involved and no sub tables are involved, the following data sources are configured through YAML:
spring: shardingsphere: sharding: binding-tables: order,order_item datasource: names: ds0,ds1 ds0: auto: true url: jdbc:mysql://localhost:3306/ds0?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&serverTimezone=Asia/Shanghai username: password: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver initial-size: 10 max-active: 100 min-idle: 2 validation-query: SELECT 1 test-on-borrow: false test-while-idle: true ds1: auto: true url: jdbc:mysql://localhost:3306/ds1?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&serverTimezone=Asia/Shanghai username: password: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver initial-size: 10 max-active: 100 min-idle: 2 validation-query: SELECT 1 test-on-borrow: false test-while-idle: true
3. Sharding slice configuration related class codes:
DataSourceConfig.java: get data source
DatabaseShardingAlgorithm.java: database sorting algorithm
MybatisConfig.java: sub database configuration
3.1 DataSourceConfig.java
import javax.sql.DataSource; 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 com.alibaba.druid.pool.DruidDataSource; @Configuration public class DataSourceConfig { @Bean @Qualifier("ds0") @ConfigurationProperties("spring.shardingsphere.datasource.ds0") public DataSource ds0DataSource() { return new DruidDataSource(); } @Bean @Qualifier("ds1DataSource") @ConfigurationProperties("spring.shardingsphere.datasource.ds1") public DataSource ds1DataSource() { return new DruidDataSource(); } ......Other data sources }
3.2 DatabaseShardingAlgorithm.java
import java.util.Collection; import org.apache.shardingsphere.api.sharding.standard.PreciseShardingAlgorithm; import org.apache.shardingsphere.api.sharding.standard.PreciseShardingValue; import com.jiaoyan.tiku.enumeration.DataSourceEnum; import lombok.extern.slf4j.Slf4j; @Slf4j public class DatabaseShardingAlgorithm implements PreciseShardingAlgorithm<Integer> { /** * Sharding. * * @param collection * Data source (Library) name collection or real table name collection. * * @param preciseShardingValue * The value of the slice key. * * @return The name of the data source (Library) to be used by the routed SQL or the name of the real table to be used by the routed SQL * Note: here is the name of the data source (Library) to be used by the routed SQL. */ @Override public String doSharding(Collection<String> collection, PreciseShardingValue<Integer> preciseShardingValue) { //log.info("DatabaseShardingAlgorithm collection:{}, preciseShardingValue: {}", collection, preciseShardingValue); return preciseShardingValue.getValue() % 2; } // public class DemoDatabaseShardingAlgorithm implements SingleKeyDatabaseShardingAlgorithm<Long> { // // @Override // public String doEqualSharding(Collection<String> databaseNames, ShardingValue<Long> shardingValue) { // // for (String each : databaseNames) { // if (each.endsWith(Long.parseLong(shardingValue.getValue().toString()) % 2 + "")) { // return each; // } // } // throw new IllegalArgumentException(); // } // // @Override // public Collection<String> doInSharding(Collection<String> databaseNames, ShardingValue<Long> shardingValue) { // Collection<String> result = new LinkedHashSet<>(databaseNames.size()); // for (Long value : shardingValue.getValues()) { // for (String tableName : databaseNames) { // if (tableName.endsWith(value % 2 + "")) { // result.add(tableName); // } // } // } // return result; // } // // @Override // public Collection<String> doBetweenSharding(Collection<String> databaseNames, ShardingValue<Long> shardingValue) { // Collection<String> result = new LinkedHashSet<>(databaseNames.size()); // Range<Long> range = (Range<Long>) shardingValue.getValueRange(); // for (Long i = range.lowerEndpoint(); i <= range.upperEndpoint(); i++) { // for (String each : databaseNames) { // if (each.endsWith(i % 2 + "")) { // result.add(each); // } // } // } // return result; // } }
3.3 MybatisConfig.java
import java.sql.SQLException; import java.util.List; import java.util.Map; import java.util.Properties; import javax.annotation.Resource; import javax.sql.DataSource; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.shardingsphere.api.config.sharding.ShardingRuleConfiguration; import org.apache.shardingsphere.api.config.sharding.TableRuleConfiguration; import org.apache.shardingsphere.api.config.sharding.strategy.StandardShardingStrategyConfiguration; import org.apache.shardingsphere.shardingjdbc.api.ShardingDataSourceFactory; import org.assertj.core.util.Lists; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.SqlSessionTemplate; import org.mybatis.spring.annotation.MapperScan; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.ResourceLoader; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.core.io.support.ResourcePatternResolver; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.transaction.annotation.EnableTransactionManagement; import com.google.common.collect.Maps; import com.jiaoyan.tiku.enumeration.DataSourceEnum; @Configuration @ComponentScan(basePackageClasses = MybatisConfig.class) @MapperScan(basePackages = "com.shopping.dao", sqlSessionTemplateRef = "sqlSessionTemplate") @EnableTransactionManagement @AutoConfigureAfter({DataSourceConfig.class}) public class MybatisConfig { private static final String MAPPER_LOCATION = "classpath:mapper/**/*.xml"; private static final String TYPE_ALIAS_PACKAGE = "com.shopping.dao"; private static final String MYBATIS_CONFIG_LOCATION = "classpath:mybatis/mybatis.xml"; private static final String DATABASE_PREFIX = "ds"; private static final String ORDER_ID = "order_id"; @Resource(name = "ds0DataSource") private DataSource ds0DataSource; @Resource(name = "ds1DataSource") private DataSource ds1DataSource; @Autowired private ResourceLoader resourceLoader; @Value("${spring.shardingsphere.sharding.binding-tables}") private String tables; @Value("${spring.shardingsphere.datasource.names}") private String databases; @Bean public DataSource dataSource() throws SQLException { ShardingRuleConfiguration shardingRuleConfig = new ShardingRuleConfiguration(); shardingRuleConfig.getBindingTableGroups().add(tables); shardingRuleConfig.getTableRuleConfigs().addAll(getTableRule()); shardingRuleConfig.setDefaultDatabaseShardingStrategyConfig( new StandardShardingStrategyConfiguration(ORDER_ID, new DatabaseShardingAlgorithm())); return ShardingDataSourceFactory.createDataSource(roundRobinDataSourceProxy(), shardingRuleConfig, new Properties()); } private List<TableRuleConfiguration> getTableRule() { String[] tableNameList = tables.split(","); String[] databaseNameList = databases.split(","); int databaseSize = databaseNameList.length; List<TableRuleConfiguration> tableRuleList = Lists.newArrayList(); for (String tableName : tableNameList) { tableRuleList.add(getTableRule(tableName, databaseSize)); } return tableRuleList; } private TableRuleConfiguration getTableRule(String tableName, int databaseSize) { // ds${0..1}.tableName return new TableRuleConfiguration(tableName, DATABASE_PREFIX + "${0.." + (databaseSize - 1) + "}." + tableName); } public Map<String, DataSource> roundRobinDataSourceProxy() { Map<String, DataSource> targetDataSourceMap = Maps.newHashMap(); targetDataSourceMap.put(DATABASE_PREFIX + DataSourceEnum.ds0.getIndex(), ds0DataSource); targetDataSourceMap.put(DATABASE_PREFIX + DataSourceEnum.ds1.getIndex(), ds1DataSource); return targetDataSourceMap; } @Bean public DataSourceTransactionManager transactionManager() throws SQLException { return new DataSourceTransactionManager(dataSource()); } @Bean public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) { return new SqlSessionTemplate(sqlSessionFactory); } @Bean public SqlSessionFactory sqlSessionFactory() throws Exception { SqlSessionFactoryBean bean = new SqlSessionFactoryBean(); bean.setTypeAliasesPackage(TYPE_ALIAS_PACKAGE); bean.setConfigLocation(resourceLoader.getResource(MYBATIS_CONFIG_LOCATION)); bean.setDataSource(dataSource()); bean.setFailFast(true); //Add XML directory ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); try { bean.setMapperLocations(resolver.getResources(MAPPER_LOCATION)); return bean.getObject(); } catch (Exception e) { throw new RuntimeException(e); } } }
4. Startup class configuration
Because the DataSource is customized, you need to exclude the DataSource provided by SpringBoot by default:
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
3, Pit avoidance Guide
1,Java -version 1.8.0_291 version, startup error, change to 1.8.0_ 211 is no problem.
4, Reference documents
1. ShardingSphere configuration manual: https://shardingsphere.apache.org/document/legacy/4.x/document/cn/manual/sharding-jdbc/configuration/
2. Shardingsphere example project Git address: https://github.com/geomonlin/incubator-shardingsphere-example
3. Sub database & sub table of ShardingSphere: https://blog.csdn.net/justry_deng/article/details/90815986