本文介紹 Sharding-JDBC 數(shù)據(jù)源分片之使用 Java 配置實現(xiàn)基于原生 JDBC 的范圍分片方案频轿。
注意:請先閱讀 【Sharding-JDBC 數(shù)據(jù)源分片:Java 配置實現(xiàn)基于原生 JDBC 的精確分片方案】疟丙,本文示例代碼在此基礎(chǔ)上增量添加奠滑。
目錄
- 開發(fā)環(huán)境
- 基礎(chǔ)示例
- 總結(jié)
開發(fā)環(huán)境
- Oracle JDK 1.8.0_201
- Apache Maven 3.6.0
- IntelliJ IDEA (Version 2018.3.3)
- MySQL 5.6.38
基礎(chǔ)示例
- 定義精確分片算法接口
PreciseShardingAlgorithm
實現(xiàn)。
package tutorial.shardingsphere.jdbc.algorithm;
import org.apache.shardingsphere.api.sharding.standard.PreciseShardingAlgorithm;
import org.apache.shardingsphere.api.sharding.standard.PreciseShardingValue;
import java.util.Collection;
public final class PreciseModuloShardingDatabaseAlgorithm implements PreciseShardingAlgorithm<Long> {
@Override
public String doSharding(Collection<String> dataSourceNames, PreciseShardingValue<Long> preciseShardingValue) {
for (String dataSourceName : dataSourceNames) {
if (dataSourceName.endsWith(preciseShardingValue.getValue() % 2 + "")) {
return dataSourceName;
}
}
throw new UnsupportedOperationException();
}
}
- 定義范圍分片算法接口
RangeShardingAlgorithm
實現(xiàn)室抽。
package tutorial.shardingsphere.jdbc.algorithm;
import com.google.common.collect.Range;
import org.apache.shardingsphere.api.sharding.standard.RangeShardingAlgorithm;
import org.apache.shardingsphere.api.sharding.standard.RangeShardingValue;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.Set;
public final class RangeModuloShardingDatabaseAlgorithm implements RangeShardingAlgorithm<Integer> {
@Override
public Collection<String> doSharding(Collection<String> dataSourceNames, RangeShardingValue<Integer> rangeShardingValue) {
Set<String> result = new LinkedHashSet<>();
if (Range.closed(1, 5).encloses(rangeShardingValue.getValueRange())) {
for (String dataSourceName : dataSourceNames) {
if (dataSourceName.endsWith("0")) {
result.add(dataSourceName);
}
}
} else if (Range.closed(6, 10).encloses(rangeShardingValue.getValueRange())) {
for (String dataSourceName : dataSourceNames) {
if (dataSourceName.endsWith("1")) {
result.add(dataSourceName);
}
}
} else if (Range.closed(1, 10).encloses(rangeShardingValue.getValueRange())) {
result.addAll(dataSourceNames);
} else {
throw new UnsupportedOperationException();
}
return result;
}
}
- 定義獲取數(shù)據(jù)源的工廠類搪哪。
package tutorial.shardingsphere.jdbc.util;
import org.apache.shardingsphere.api.config.sharding.KeyGeneratorConfiguration;
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 tutorial.shardingsphere.jdbc.algorithm.PreciseModuloShardingDatabaseAlgorithm;
import tutorial.shardingsphere.jdbc.algorithm.RangeModuloShardingDatabaseAlgorithm;
import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
public class RangeDataSourceFactory {
/**
* 配置數(shù)據(jù)源映射
*/
private static Map<String, DataSource> createDataSourceMap() {
Map<String, DataSource> result = new HashMap<>();
result.put("ds_0", DataSourceUtils.createDataSource("ds_0"));
result.put("ds_1", DataSourceUtils.createDataSource("ds_1"));
return result;
}
public static DataSource getDataSource() throws SQLException {
// 配置數(shù)據(jù)源映射
Map<String, DataSource> dataSourceMap = createDataSourceMap();
// 配置表規(guī)則
TableRuleConfiguration tableRuleConfiguration = new TableRuleConfiguration("t_order");
tableRuleConfiguration.setKeyGeneratorConfig(new KeyGeneratorConfiguration("SNOWFLAKE", "order_id"));
// 配置分片規(guī)則
ShardingRuleConfiguration shardingRuleConfiguration = new ShardingRuleConfiguration();
shardingRuleConfiguration.getTableRuleConfigs().add(tableRuleConfiguration);
// 配置默認(rèn)分庫策略
shardingRuleConfiguration.setDefaultDatabaseShardingStrategyConfig(
new StandardShardingStrategyConfiguration("user_id",
new PreciseModuloShardingDatabaseAlgorithm(),
new RangeModuloShardingDatabaseAlgorithm())
);
// 獲取數(shù)據(jù)源對象
return ShardingDataSourceFactory.createDataSource(dataSourceMap, shardingRuleConfiguration, new Properties());
}
}
與【Sharding-JDBC 數(shù)據(jù)源分片:Java 配置實現(xiàn)基于原生 JDBC 的精確分片方案】 中定義的 DataSourceFactory
唯一區(qū)別在于配置的默認(rèn)分庫策略不同,請注意 StandardShardingStrategyConfiguration
構(gòu)造方法坪圾。
- 定義新的
Order
(訂單)數(shù)據(jù)訪問實現(xiàn)晓折,繼承OrderDaoImpl
,重寫select
方法兽泄。
package tutorial.shardingsphere.jdbc.dao.impl;
import tutorial.shardingsphere.jdbc.bean.Order;
import javax.sql.DataSource;
import java.util.List;
public class RangeOrderDaoImpl extends OrderDaoImpl {
public RangeOrderDaoImpl(DataSource dataSource) {
super(dataSource);
}
@Override
public List<Order> select() {
String sql = "SELECT * FROM t_order WHERE user_id BETWEEN 1 AND 5";
return listOrders(sql);
}
}
- 編寫單元測試漓概。
package tutorial.shardingsphere.jdbc;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import tutorial.shardingsphere.jdbc.bean.Order;
import tutorial.shardingsphere.jdbc.dao.IOrderDao;
import tutorial.shardingsphere.jdbc.dao.impl.RangeOrderDaoImpl;
import tutorial.shardingsphere.jdbc.util.RangeDataSourceFactory;
import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.List;
public class JdbcConfigRangeShardingDatabaseTest {
private static IOrderDao orderDao;
@BeforeClass
public static void init() throws SQLException {
DataSource dataSource = RangeDataSourceFactory.getDataSource();
orderDao = new RangeOrderDaoImpl(dataSource);
}
@Test
public void test() {
orderDao.createTableIfNotExists();
orderDao.truncateTable();
Assert.assertEquals(0, orderDao.select().size());
for (long i = 1; i <= 10; i++) {
Order order = new Order(i, "Order " + i);
orderDao.insert(order);
}
List<Order> result = orderDao.select();
result.forEach(System.out::println);
}
}
測試結(jié)果:
Order{orderId=350292866448228352, userId=2, details='Order 2'}
Order{orderId=350292866498560000, userId=4, details='Order 4'}
說明:使用 t_order
表中 user_id
字段作為單一分片鍵,使用 user_id
值對 2 做取模運算病梢,余 0 的存儲在 ds_0
中胃珍,余 1 的存儲在 ds_1
中,因此以上測試中第 1蜓陌、3觅彰、5、7护奈、9 個訂單會存儲在 ds_1
中缔莲,第 2、4霉旗、6痴奏、8、10 個訂單會存儲在 ds_0
中厌秒。按照定義的范圍分片算法邏輯读拆,當(dāng) BETWEEN AND
數(shù)據(jù)范圍在 1-5 之間時只會在 ds_0
中查找,數(shù)據(jù)范圍在 6-10 之間時只會在 ds_1
中查找鸵闪。覆蓋后的 DAO 查詢條件是 BETWEEN 1 AND 5
檐晕,因此只會在 ds_0
中查找,只能找到第 2 個和第 4 個訂單蚌讼。
將 RangeOrderDaoImpl
的 select
方法查詢條件修改為 BETWEEN 1 AND 10
辟灰,重新執(zhí)行單元測試可以查詢到已插入的所有訂單信息,測試結(jié)果略篡石。
如果查詢范圍超過 1-10芥喇,如 BETWEEN 1 AND 11
,則執(zhí)行查詢會報以下異常凰萨。
java.lang.UnsupportedOperationException
at tutorial.shardingsphere.jdbc.algorithm.RangeModuloShardingDatabaseAlgorithm.doSharding(RangeModuloShardingDatabaseAlgorithm.java:31)
at org.apache.shardingsphere.core.strategy.route.standard.StandardShardingStrategy.doSharding(StandardShardingStrategy.java:71)
at org.apache.shardingsphere.core.strategy.route.standard.StandardShardingStrategy.doSharding(StandardShardingStrategy.java:60)
at org.apache.shardingsphere.core.route.type.standard.StandardRoutingEngine.routeDataSources(StandardRoutingEngine.java:191)
at org.apache.shardingsphere.core.route.type.standard.StandardRoutingEngine.route(StandardRoutingEngine.java:178)
at org.apache.shardingsphere.core.route.type.standard.StandardRoutingEngine.routeByShardingConditionsWithCondition(StandardRoutingEngine.java:108)
at org.apache.shardingsphere.core.route.type.standard.StandardRoutingEngine.routeByShardingConditions(StandardRoutingEngine.java:102)
at org.apache.shardingsphere.core.route.type.standard.StandardRoutingEngine.getDataNodes(StandardRoutingEngine.java:87)
at org.apache.shardingsphere.core.route.type.standard.StandardRoutingEngine.route(StandardRoutingEngine.java:69)
at org.apache.shardingsphere.core.route.router.sharding.ParsingSQLRouter.route(ParsingSQLRouter.java:106)
at org.apache.shardingsphere.core.route.PreparedStatementRoutingEngine.route(PreparedStatementRoutingEngine.java:66)
at org.apache.shardingsphere.core.PreparedQueryShardingEngine.route(PreparedQueryShardingEngine.java:60)
at org.apache.shardingsphere.core.BaseShardingEngine.shard(BaseShardingEngine.java:64)
at org.apache.shardingsphere.shardingjdbc.jdbc.core.statement.ShardingPreparedStatement.shard(ShardingPreparedStatement.java:224)
at org.apache.shardingsphere.shardingjdbc.jdbc.core.statement.ShardingPreparedStatement.executeQuery(ShardingPreparedStatement.java:109)
at tutorial.shardingsphere.jdbc.dao.impl.OrderDaoImpl.listOrders(OrderDaoImpl.java:82)
at tutorial.shardingsphere.jdbc.dao.impl.RangeOrderDaoImpl.select(RangeOrderDaoImpl.java:17)
at tutorial.shardingsphere.jdbc.RangeShardingDatabaseTest.test(RangeShardingDatabaseTest.java:29)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
總結(jié)
- 分片算法支持通過
=
继控、IN
、BETWEEN
進(jìn)行數(shù)據(jù)分片胖眷,需要自定義實現(xiàn)武通。 - 精確分片算法
PreciseShardingAlgorithm
用于處理使用單一鍵作為分片鍵的=
和IN
進(jìn)行分片的場景,需要配合StandardShardingStrategy
使用珊搀。 - 范圍分片算法
RangeShardingAlgorithm
用于處理使用單一鍵作為分片鍵的BETWEEN AND
進(jìn)行分片的場景冶忱,需要配合StandardShardingStrategy
使用。