demo源码 https://github.com/wzjwhut/spring-jdbc-sharding
原理和开源库
原理: 通过解析sql语句, 根据分库分表的规则,替换掉表名和库名
文档资料 https://shardingsphere.apache.org/document/current/en/manual/sharding-jdbc/usage/sharding/
对应的SQL解析器为SQLParsingEngine
, 有兴趣可以细看一下
使用方法
准备工具
安装mysql, 准备好需要的数据库和表
https://github.com/wzjwhut/spring-jdbc-sharding/blob/master/src/main/resources/config.sql
maven依赖
<dependency>
<groupId>com.dangdang</groupId>
<artifactId>sharding-jdbc-core</artifactId>
<version>1.5.4.1</version>
</dependency>
配置DataSource
使用jdbc-sharding的data source将spring的data source包裹起来
import com.dangdang.ddframe.rdb.sharding.api.ShardingDataSourceFactory;
import com.dangdang.ddframe.rdb.sharding.api.rule.DataSourceRule;
import com.dangdang.ddframe.rdb.sharding.api.rule.ShardingRule;
import com.dangdang.ddframe.rdb.sharding.api.rule.TableRule;
import com.dangdang.ddframe.rdb.sharding.api.strategy.database.DatabaseShardingStrategy;
import com.dangdang.ddframe.rdb.sharding.api.strategy.table.TableShardingStrategy;
import com.dangdang.ddframe.rdb.sharding.keygen.DefaultKeyGenerator;
import com.dangdang.ddframe.rdb.sharding.keygen.KeyGenerator;
import com.mysql.jdbc.Driver;
import com.zaxxer.hikari.HikariDataSource;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class DataSourceConfiguration {
private final static Logger log = LogManager.getLogger(DataSourceConfiguration.class);
@Bean
public DataSource getDataSource() throws SQLException {
return buildDataSource();
}
private DataSource buildDataSource() throws SQLException {
// 设置分库映射
Map<String, DataSource> dataSourceMap = new HashMap<>(2);
//分库列表
dataSourceMap.put("database0", createDataSource("database0"));
dataSourceMap.put("database1", createDataSource("database1"));
//默认库
DataSourceRule dataSourceRule = new DataSourceRule(dataSourceMap, "database0");
TableRule orderTableRule = TableRule.builder("person")
.actualTables(Arrays.asList("person_0", "person_1"))
.dataSourceRule(dataSourceRule)
.build();
//分库分表策略
ShardingRule shardingRule = ShardingRule.builder()
.dataSourceRule(dataSourceRule)
.tableRules(Arrays.asList(orderTableRule))
.databaseShardingStrategy(new DatabaseShardingStrategy("id", new ModuleDatabaseShardingAlgorithm()))
.tableShardingStrategy(new TableShardingStrategy("id", new ModuleTableShardingAlgorithm())).build();
DataSource dataSource = ShardingDataSourceFactory.createDataSource(shardingRule);
return dataSource;
}
private static DataSource createDataSource(final String dataSourceName) {
log.info("create data source: {}", dataSourceName);
HikariDataSource result = new HikariDataSource();
result.setJdbcUrl(String.format("jdbc:mysql://localhost:3306/%s", dataSourceName));
result.setDriverClassName(Driver.class.getName());
result.setUsername("root");
result.setPassword("123456");
return result;
}
// @Bean
// public KeyGenerator keyGenerator() {
// return new DefaultKeyGenerator();
// }
}
配置分库
import com.dangdang.ddframe.rdb.sharding.api.ShardingValue;
import com.dangdang.ddframe.rdb.sharding.api.strategy.database.MultipleKeysDatabaseShardingAlgorithm;
import com.dangdang.ddframe.rdb.sharding.api.strategy.database.SingleKeyDatabaseShardingAlgorithm;
import com.google.common.collect.Range;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.Collection;
import java.util.LinkedHashSet;
/**
* 分库算法
* 多键分片 {@link com.dangdang.ddframe.rdb.sharding.api.strategy.database.MultipleKeysDatabaseShardingAlgorithm}
*/
public class ModuleDatabaseShardingAlgorithm implements SingleKeyDatabaseShardingAlgorithm<Long> {
private final static Logger log = LogManager.getLogger(ModuleDatabaseShardingAlgorithm.class);
//sql出现等于条件时,执行
@Override
public String doEqualSharding(Collection<String> dbNames, ShardingValue<Long> shardingValue) {
//数据库列表,本例为[database0, database1]
log.info("[doEqualSharding] availableTargetNames: {}", dbNames);
//分库字段的值,本例为person的id值
log.info("[doEqualSharding] shardingValue.getValue(): {}", shardingValue.getValue());
//分库策略的类型. 本例为SINGLE
log.info("[doEqualSharding] shardingValue.getType(): {}", shardingValue.getType());
//分库字段的名称, 本例为id
log.info("[doEqualSharding] shardingValue.getColumnName(): {}", shardingValue.getColumnName());
//表的逻辑名称, 本例为person
log.info("[doEqualSharding] shardingValue.getLogicTableName(): {}", shardingValue.getLogicTableName());
//根据字段的值,选出一个数据库
//临时写法
for (String dbName : dbNames) {
if (dbName.endsWith(String.valueOf(shardingValue.getValue() % 2 ))) {
return dbName;
}
}
throw new IllegalArgumentException();
}
//sql的出现的in条件时,执行
@Override
public Collection<String> doInSharding(Collection<String> dbNames, ShardingValue<Long> shardingValue) {
log.info("[doInSharding] values: {}", shardingValue.getValues());
Collection<String> result = new LinkedHashSet<>(dbNames.size());
for (Long value : shardingValue.getValues()) {
for (String dbName : dbNames) {
if (dbName.endsWith(value % 2 + "")) {
result.add(dbName);
}
}
}
return result;
}
//sql的出现between条件时,执行
@Override
public Collection<String> doBetweenSharding(Collection<String> dbNames,
ShardingValue<Long> shardingValue) {
log.info("[doBetweenSharding] values: {}", shardingValue.getValueRange());
Collection<String> result = new LinkedHashSet<>(dbNames.size());
Range<Long> range = shardingValue.getValueRange();
for (Long i = range.lowerEndpoint(); i <= range.upperEndpoint(); i++) {
for (String dbName : dbNames) {
if (dbName.endsWith(i % 2 + "")) {
result.add(dbName);
}
}
}
return result;
}
}
配置分表
和分库的算法几乎一样。
import com.dangdang.ddframe.rdb.sharding.api.ShardingValue;
import com.dangdang.ddframe.rdb.sharding.api.strategy.table.SingleKeyTableShardingAlgorithm;
import com.google.common.collect.Range;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.Collection;
import java.util.LinkedHashSet;
public final class ModuleTableShardingAlgorithm implements SingleKeyTableShardingAlgorithm<Long> {
private final static Logger log = LogManager.getLogger(ModuleTableShardingAlgorithm.class);
@Override
public String doEqualSharding(final Collection<String> tableNames, final ShardingValue<Long> shardingValue) {
log.info("doEqualSharding, tables: {}", tableNames);
log.info("[doEqualSharding] shardingValue.getValue(): {}", shardingValue.getValue());
log.info("[doEqualSharding] shardingValue.getType(): {}", shardingValue.getType());
log.info("[doEqualSharding] shardingValue.getColumnName(): {}", shardingValue.getColumnName());
log.info("[doEqualSharding] shardingValue.getLogicTableName(): {}", shardingValue.getLogicTableName());
//临时写法
for (String tableName : tableNames) {
log.info("[doEqualSharding] availableTargetNames: {}", tableName);
if (tableName.endsWith(shardingValue.getValue() % 2 + "")) {
return tableName;
}
}
throw new IllegalArgumentException();
}
@Override
public Collection<String> doInSharding(final Collection<String> tableNames, final ShardingValue<Long> shardingValue) {
log.info("doInSharding");
Collection<String> result = new LinkedHashSet<>(tableNames.size());
for (Long value : shardingValue.getValues()) {
for (String tableName : tableNames) {
if (tableName.endsWith(value % 2 + "")) {
result.add(tableName);
}
}
}
return result;
}
@Override
public Collection<String> doBetweenSharding(final Collection<String> tableNames, final ShardingValue<Long> shardingValue) {
log.info("doBetweenSharding");
Collection<String> result = new LinkedHashSet<>(tableNames.size());
Range<Long> range = shardingValue.getValueRange();
for (Long i = range.lowerEndpoint(); i <= range.upperEndpoint(); i++) {
for (String tableName : tableNames) {
if (tableName.endsWith(i % 2 + "")) {
result.add(tableName);
}
}
}
return result;
}
}