1. 程式人生 > >Sharding jdbc 強制路由策略(HintShardingStrategy)使用記錄

Sharding jdbc 強制路由策略(HintShardingStrategy)使用記錄

理解 new sim fin columns 時間 隨著 不知道 ring

背景

隨著項目運行時間逐漸增加,數據庫中的數據也越來越多,雖然加索引,優化查詢,但是數據量太大,還是會影響查詢效率,也給數據庫增加了負載。
再加上冷數據基本不使用的場景,決定采用分表來處理數據,從而來提高系統性能。

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入口在這裏,等待回復
如有不對還請大神們指教!

Sharding jdbc 強制路由策略(HintShardingStrategy)使用記錄