背景
隨着項目運行時間逐漸增加,數據庫中的數據也越來越多,雖然加索引,優化查詢,但是數據量太大,還是會影響查詢效率,也給數據庫增加了負載。
再加上冷數據基本不使用的場景,決定采用分表來處理數據,從而來提高系統性能。
sharding jdbc 介紹
官方文檔在這里。
Sharding-jdbc 定位是輕量級的java框架,在java 的JDBC層提供額外功能。它使用客戶端直連數據庫,以jar包形式提供服務,無需額外部署和依賴,可理解為增強版的JDBC驅動,完全兼容JDBC和各種ORM框架。
- 適用於任何基於 java 的ORM 框架(JPA,Hibernate,mybatis 等)
- 基於任何第三方的數據庫連接池(DBCP,c3p0, druid, hikariCP 等)
- 支持任意實現JDBC規范的數據庫(目前支持MySQL,Oracle,SQLServer和PostgreSQL)---因為SQL 語法不同,有對應的解析
方案選擇
文檔很全,並且有三款產品的說明,分別是:Sharding jdbc 、Sharding-proxy、Sharding-sidecar(研發中)
本着想學技術,先實踐寫demo的原則,直接跳過SQL,解析引擎,路由、改寫、歸並引擎等說明,直接到配置界面。
配置的方式有很多種:
- java 配置
- SpringBoot 配置
- yaml 配置
- Spring 命名空間配置
配置好之后,直接傳入一個 分片算法即可。
// 配置數據源
DataSource getShardingDataSource() throws SQLException {
ShardingRuleConfiguration shardingRuleConfig = new ShardingRuleConfiguration();
shardingRuleConfig.getTableRuleConfigs().add(getOrderTableRuleConfiguration());
shardingRuleConfig.getBindingTableGroups().add("t_order");
// 如果需要打印SQL new Properties 可以 設置一個屬性 > io.shardingjdbc.core.constant.ShardingPropertiesConstant#SQL_SHOW
return ShardingDataSourceFactory.createDataSource(createDataSourceMap(), shardingRuleConfig, new ConcurrentHashMap<>(), new Properties());
}
// 針對不同的表可以有不同的策略
TableRuleConfiguration getOrderTableRuleConfiguration() {
TableRuleConfiguration result = new TableRuleConfiguration();
result.setLogicTable("t_order");
result.setActualDataNodes("ds${0..1}.t_order${0..1}");
result.setTableShardingStrategyConfig(shardingRuleConfiguration());
return result;
}
@Bean
public ShardingStrategyConfiguration shardingRuleConfiguration() {
return new MyHintShardingStrategyConfiguration("order_id","com.test.config.MyHintShardingAlgorithm");
}
Map<String, DataSource> createDataSourceMap() {
Map<String, DataSource> result = new HashMap<>();
result.put("ds0", DataSourceUtil.createDataSource("ds0"));
result.put("ds1", DataSourceUtil.createDataSource("ds1"));
return result;
}
踩坑記錄
- 情況
本來以為這樣就可以成功的分片,沒想到 MyHintShardingAlgorithm#sharding方法一直都進不去,多次都不行,只能斷點調試了。
確實發現了一個地方:
見:io.shardingjdbc.core.routing.type.simple.SimpleRoutingEngine#route 方法
// 分片路由:找到對應的表節點
@Override
public RoutingResult route() {
TableRule tableRule = shardingRule.getTableRule(logicTableName);
List<ShardingValue> databaseShardingValues = getDatabaseShardingValues(tableRule);
List<ShardingValue> tableShardingValues = getTableShardingValues(tableRule); // 獲取下面表名的地方
Collection<String> routedDataSources = routeDataSources(tableRule, databaseShardingValues);
Collection<DataNode> routedDataNodes = new LinkedList<>();
for (String each : routedDataSources) {
routedDataNodes.addAll(routeTables(tableRule, each, tableShardingValues));
}
return generateRoutingResult(routedDataNodes);
}
// sharding方法只有這里才會調用,他有一個條件,就是傳入的 tableShardingValues 不可以為空
private Collection<DataNode> routeTables(final TableRule tableRule, final String routedDataSource, final List<ShardingValue> tableShardingValues) {
Collection<String> availableTargetTables = tableRule.getActualTableNames(routedDataSource);
Collection<String> routedTables = tableShardingValues.isEmpty() ? availableTargetTables
: shardingRule.getTableShardingStrategy(tableRule).doSharding(availableTargetTables, tableShardingValues);// **這里**
Preconditions.checkState(!routedTables.isEmpty(), "no table route info");
Collection<DataNode> result = new LinkedList<>();
for (String each : routedTables) {
result.add(new DataNode(routedDataSource, each));
}
return result;
}
// 傳入的參數是這里傳入的:由於我這里使用強制路由所以走這里
private List<ShardingValue> getTableShardingValuesFromHint(final Collection<String> shardingColumns) {
List<ShardingValue> result = new ArrayList<>(shardingColumns.size());
for (String each : shardingColumns) {
Optional<ShardingValue> shardingValue = HintManagerHolder.getTableShardingValue(new ShardingKey(logicTableName, each));
if (shardingValue.isPresent()) {
result.add(shardingValue.get());
}
}
return result;
}
- 分析
shardingColumns 是從對應的 ShardingStrategy 中賦值的,我使用的強制路由源碼如下:
public HintShardingStrategy(final HintShardingAlgorithm shardingAlgorithm) {
this.shardingColumns = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
this.shardingAlgorithm = shardingAlgorithm;
}
發現shardingColumns 一直是空的,也沒有方法可以添加(自定義添加也不行,因為ShardingStrategy是通過對應的ShardingStrategyConfiguration#build方法生成的),所以肯定不會走到路由的地方。
變更
排查到這里,就找到了一個修改的方法--重寫 ShardingStrategy ,隨意給 columns 賦值不為空即可。
// 自定義新的構造函數,給 shardingColumns 賦值
public MyHintShardingStrategy(Collection<String> shardingColumns, final HintShardingAlgorithm shardingAlgorithm) {
this.shardingColumns = new TreeSet<>(shardingColumns);
this.shardingAlgorithm = shardingAlgorithm;
}
重新運行項目,發現一切按想要的來。
回顧
不知道是自己配置的原因,還是打開的方式不對,反正通過這種方式實現了表的分片。
向項目提的issue入口在這里,等待回復
如有不對還請大神們指教!
**==============20100307更新**
他們在最新的版本里修復了這個問題,從 HintManageHolder 里獲取 sharding Columns。
代碼如下:
package org.apache.shardingsphere.core.routing.type.standard;
private List<RouteValue> getTableShardingValuesFromHint() {
return getRouteValues(HintManagerHolder.getTableShardingValues(logicTableName));
}
但是 mvn 上不知道能不能獲取到那個版本,先記錄到這里吧。
