溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Sharding-Jdbc自定義復(fù)合分片的實(shí)現(xiàn)方法

發(fā)布時(shí)間:2021-07-30 14:33:09 來源:億速云 閱讀:164 作者:chen 欄目:開發(fā)技術(shù)

這篇文章主要講解了“Sharding-Jdbc自定義復(fù)合分片的實(shí)現(xiàn)方法”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“Sharding-Jdbc自定義復(fù)合分片的實(shí)現(xiàn)方法”吧!

目錄
  • Sharding-JDBC的數(shù)據(jù)分片策略

    • 分片鍵

    • 分片算法

    • 分片策略

    • SQL Hint

  • 實(shí)戰(zhàn)–自定義復(fù)合分片策略

    • 小結(jié)

      Sharding-JDBC中的分片策略有兩個(gè)維度,分別是:

      • 數(shù)據(jù)源分片策略(DatabaseShardingStrategy)

      • 表分片策略(TableShardingStrategy)

      其中,數(shù)據(jù)源分片策略表示:數(shù)據(jù)路由到的物理目標(biāo)數(shù)據(jù)源,表分片策略表示數(shù)據(jù)被路由到的目標(biāo)表。

      特別的,表分片策略是依賴于數(shù)據(jù)源分片策略的,也就是說要先分庫再分表,當(dāng)然也可以只分表。

      Sharding-Jdbc自定義復(fù)合分片的實(shí)現(xiàn)方法

      Sharding-JDBC的數(shù)據(jù)分片策略

      Sharding-JDBC的分片策略包含了分片鍵和分片算法。由于分片算法與業(yè)務(wù)實(shí)現(xiàn)緊密相關(guān),因此Sharding-JDBC沒有提供內(nèi)置的分片算法,而是通過分片策略將各種場(chǎng)景提煉出來,提供了高層級(jí)的抽象,通過提供接口讓開發(fā)者自行實(shí)現(xiàn)分片算法。

      以下內(nèi)容引用自官方文檔。官方文檔

      首先介紹四種分片算法。

      通過分片算法將數(shù)據(jù)分片,支持通過=、BETWEEN和IN分片。
      分片算法需要應(yīng)用方開發(fā)者自行實(shí)現(xiàn),可實(shí)現(xiàn)的靈活度非常高。

      目前提供4種分片算法。由于分片算法和業(yè)務(wù)實(shí)現(xiàn)緊密相關(guān),
      因此并未提供內(nèi)置分片算法,而是通過分片策略將各種場(chǎng)景提煉出來,
      提供更高層級(jí)的抽象,并提供接口讓應(yīng)用開發(fā)者自行實(shí)現(xiàn)分片算法。

      分片鍵

      用于分片的數(shù)據(jù)庫字段,是將數(shù)據(jù)庫(表)水平拆分的關(guān)鍵字段。例:將訂單表中的訂單主鍵的尾數(shù)取模分片,則訂單主鍵為分片字段。 SQL中如果無分片字段,將執(zhí)行全路由,性能較差。 除了對(duì)單分片字段的支持,ShardingSphere也支持根據(jù)多個(gè)字段進(jìn)行分片。

      分片算法

      通過分片算法將數(shù)據(jù)分片,支持通過=BETWEENIN分片。分片算法需要應(yīng)用方開發(fā)者自行實(shí)現(xiàn),可實(shí)現(xiàn)的靈活度非常高。

      目前提供4種分片算法。由于分片算法和業(yè)務(wù)實(shí)現(xiàn)緊密相關(guān),因此并未提供內(nèi)置分片算法,而是通過分片策略將各種場(chǎng)景提煉出來,提供更高層級(jí)的抽象,并提供接口讓應(yīng)用開發(fā)者自行實(shí)現(xiàn)分片算法。

      精確分片算法

      對(duì)應(yīng)PreciseShardingAlgorithm,用于處理使用單一鍵作為分片鍵的=與IN進(jìn)行分片的場(chǎng)景。需要配合StandardShardingStrategy使用。

      范圍分片算法

      對(duì)應(yīng)RangeShardingAlgorithm,用于處理使用單一鍵作為分片鍵的BETWEEN AND進(jìn)行分片的場(chǎng)景。需要配合StandardShardingStrategy使用。

      復(fù)合分片算法

      對(duì)應(yīng)ComplexKeysShardingAlgorithm,用于處理使用多鍵作為分片鍵進(jìn)行分片的場(chǎng)景,包含多個(gè)分片鍵的邏輯較復(fù)雜,需要應(yīng)用開發(fā)者自行處理其中的復(fù)雜度。需要配合ComplexShardingStrategy使用。

      Hint分片算法

      對(duì)應(yīng)HintShardingAlgorithm,用于處理使用Hint行分片的場(chǎng)景。需要配合HintShardingStrategy使用。

      分片策略

      包含分片鍵和分片算法,由于分片算法的獨(dú)立性,將其獨(dú)立抽離。真正可用于分片操作的是分片鍵 + 分片算法,也就是分片策略。目前提供5種分片策略。

      標(biāo)準(zhǔn)分片策略

      對(duì)應(yīng)StandardShardingStrategy。提供對(duì)SQL語句中的=, IN和BETWEEN AND的分片操作支持。StandardShardingStrategy只支持單分片鍵,提供PreciseShardingAlgorithm和RangeShardingAlgorithm兩個(gè)分片算法。PreciseShardingAlgorithm是必選的,用于處理=和IN的分片。RangeShardingAlgorithm是可選的,用于處理BETWEEN AND分片,如果不配置RangeShardingAlgorithm,SQL中的BETWEEN AND將按照全庫路由處理。

      復(fù)合分片策略

      對(duì)應(yīng)ComplexShardingStrategy。復(fù)合分片策略。提供對(duì)SQL語句中的=, IN和BETWEEN AND的分片操作支持。ComplexShardingStrategy支持多分片鍵,由于多分片鍵之間的關(guān)系復(fù)雜,因此并未進(jìn)行過多的封裝,而是直接將分片鍵值組合以及分片操作符透傳至分片算法,完全由應(yīng)用開發(fā)者實(shí)現(xiàn),提供最大的靈活度。

      行表達(dá)式分片策略

      對(duì)應(yīng)InlineShardingStrategy。使用Groovy的表達(dá)式,提供對(duì)SQL語句中的=和IN的分片操作支持,只支持單分片鍵。對(duì)于簡單的分片算法,可以通過簡單的配置使用,從而避免繁瑣的Java代碼開發(fā),如:t_user_$->{u_id % 8}表示t_user表根據(jù)u_id模8,而分成8張表,表名稱為t_user_0t_user_7

      Hint分片策略

      對(duì)應(yīng)HintShardingStrategy。通過Hint而非SQL解析的方式分片的策略。

      不分片策略

      對(duì)應(yīng)NoneShardingStrategy。不分片的策略。

      SQL Hint

      對(duì)于分片字段非SQL決定,而由其他外置條件決定的場(chǎng)景,可使用SQL Hint靈活的注入分片字段。例:內(nèi)部系統(tǒng),按照員工登錄主鍵分庫,而數(shù)據(jù)庫中并無此字段。SQL Hint支持通過Java API和SQL注釋(待實(shí)現(xiàn))兩種方式使用。

      實(shí)戰(zhàn)–自定義復(fù)合分片策略

      由于目的為貼近實(shí)戰(zhàn),因此著重講解如何實(shí)現(xiàn)復(fù)雜分片策略,即實(shí)現(xiàn)ComplexShardingStrategy接口定制生產(chǎn)可用的分片策略。

      AdminIdShardingAlgorithm 復(fù)合分片算法代碼如下:

      import com.google.common.collect.Range;
      import io.shardingjdbc.core.api.algorithm.sharding.ListShardingValue;
      import io.shardingjdbc.core.api.algorithm.sharding.PreciseShardingValue;
      import io.shardingjdbc.core.api.algorithm.sharding.RangeShardingValue;
      import io.shardingjdbc.core.api.algorithm.sharding.ShardingValue;
      import io.shardingjdbc.core.api.algorithm.sharding.complex.ComplexKeysShardingAlgorithm;
      import org.apache.commons.lang.StringUtils;
      import org.apache.log4j.Logger;
       
      import java.util.*;
       
      /**
       */
      public class AdminIdShardingAlgorithm implements ComplexKeysShardingAlgorithm {
       
          private Logger logger = Logger.getLogger(getClass());
       
          @Override
          public Collection<String> doSharding(Collection<String> availableTargetNames, Collection<ShardingValue> shardingValues) {
              Collection<String> routTables = new HashSet<String>();
              if (shardingValues != null) {
                  for (ShardingValue shardingValue : shardingValues) {
       
                      // eq in 條件
                      if (shardingValue instanceof ListShardingValue) {
                          ListShardingValue listShardingValue = (ListShardingValue) shardingValue;
                          Collection<Comparable> values = listShardingValue.getValues();
                          if (values != null) {
                              Iterator<Comparable> it = values.iterator();
                              while (it.hasNext()) {
                                  Comparable value = it.next();
                                  String routTable = getRoutTable(shardingValue.getLogicTableName(), value);
                                  if (StringUtils.isNotBlank(routTable)) {
                                      routTables.add(routTable);
                                  }
                              }
                          }
       
                          // eq 條件
                      } else if (shardingValue instanceof PreciseShardingValue) {
                          PreciseShardingValue preciseShardingValue = (PreciseShardingValue) shardingValue;
       
                          Comparable value = preciseShardingValue.getValue();
                          String routTable = getRoutTable(shardingValue.getLogicTableName(), value);
                          if (StringUtils.isNotBlank(routTable)) {
                              routTables.add(routTable);
                          }
                          // between 條件
                      } else if (shardingValue instanceof RangeShardingValue) {
                          RangeShardingValue rangeShardingValue = (RangeShardingValue) shardingValue;
                          Range<Comparable> valueRange = rangeShardingValue.getValueRange();
                          Comparable lowerEnd = valueRange.lowerEndpoint();
                          Comparable upperEnd = valueRange.upperEndpoint();
       
                          Collection<String> tables = getRoutTables(shardingValue.getLogicTableName(), lowerEnd, upperEnd);
                          if (tables != null && tables.size() > 0) {
                              routTables.addAll(tables);
                          }
                      }
       
                      if (routTables != null && routTables.size() > 0) {
                          return routTables;
                      }
                  }
              }
       
              throw new UnsupportedOperationException();
      
          }
       
          private String getRoutTable(String logicTable, Comparable keyValue) {
              Map<String, List<KeyShardingRange>> keyRangeMap = KeyShardingRangeConfig.getKeyRangeMap();
       
              List<KeyShardingRange> keyShardingRanges = keyRangeMap.get(KeyShardingRangeConfig.SHARDING_ID_KEY);
       
              if (keyValue != null && keyShardingRanges != null) {
                  if (keyValue instanceof Integer) {
                      keyValue = Long.valueOf(((Integer) keyValue).intValue());
                  }
                  for (KeyShardingRange range : keyShardingRanges) {
                      if (keyValue.compareTo(range.getMin()) >= 0 && keyValue.compareTo(range.getMax()) <= 0) {
                          return logicTable + range.getTableKey();
                      }
                  }
              }
              return null;
          }
          private Collection<String> getRoutTables(String logicTable, Comparable lowerEnd, Comparable upperEnd) {
              Map<String, List<KeyShardingRange>> keyRangeMap = KeyShardingRangeConfig.getKeyRangeMap();
       
              List<KeyShardingRange> keyShardingRanges = keyRangeMap.get(KeyShardingRangeConfig.SHARDING_CONTENT_ID_KEY);
              Set<String> routTables = new HashSet<String>();
              if (lowerEnd != null && upperEnd != null && keyShardingRanges != null) {
                  if (lowerEnd instanceof Integer) {
                      lowerEnd = Long.valueOf(((Integer) lowerEnd).intValue());
                  }
       
                  if (upperEnd instanceof Integer) {
                      upperEnd = Long.valueOf(((Integer) upperEnd).intValue());
                  }
                  boolean start = false;
                  for (KeyShardingRange range : keyShardingRanges) {
                      if (lowerEnd.compareTo(range.getMin()) >= 0 && lowerEnd.compareTo(range.getMax()) <= 0) {
                          start = true;
                      }
                      if (start) {
                          routTables.add(logicTable + range.getTableKey());
                      }
                      if (upperEnd.compareTo(range.getMin()) >= 0 && upperEnd.compareTo(range.getMax()) <= 0) {
                          break;
                      }
                  }
              }
              return routTables;
          }
      }

      范圍 map 如下:

      import java.util.ArrayList;
      import java.util.LinkedHashMap;
      import java.util.List;
      import java.util.Map;
       
      /**
       * 分片鍵分布配置
       */
      public class KeyShardingRangeConfig {
       
          private static Map<String, List<KeyShardingRange>> keyRangeMap = new LinkedHashMap<String, List<KeyShardingRange>>();
       
          public static final String SHARDING_ORDER_ID_KEY = "id";
       
          public static final String SHARDING_USER_ID_KEY = "adminId";
       
          public static final String SHARDING_DATE_KEY = "createTime";
       
          static {
              List<KeyShardingRange> idRanges = new ArrayList<KeyShardingRange>();
              idRanges.add(new KeyShardingRange(0, "_0", 0L, 4000000L));
              idRanges.add(new KeyShardingRange(1, "_1", 4000001L, 8000000L));
              idRanges.add(new KeyShardingRange(2, "_2", 8000001L, 12000000L));
              idRanges.add(new KeyShardingRange(3, "_3", 12000001L, 16000000L));
              idRanges.add(new KeyShardingRange(4, "_4", 16000001L, 2000000L));
              keyRangeMap.put(SHARDING_ID_KEY, idRanges);
       
              List<KeyShardingRange> contentIdRanges = new ArrayList<KeyShardingRange>();
              contentIdRanges.add(new KeyShardingRange(0, "_0", 0L, 4000000L));
              contentIdRanges.add(new KeyShardingRange(1, "_1", 4000001L, 8000000L));
              contentIdRanges.add(new KeyShardingRange(2, "_2", 8000001L, 12000000L));
              contentIdRanges.add(new KeyShardingRange(3, "_3", 12000001L, 16000000L));
              contentIdRanges.add(new KeyShardingRange(4, "_4", 16000001L, 2000000L));
              keyRangeMap.put(SHARDING_CONTENT_ID_KEY, contentIdRanges);
       
              List<KeyShardingRange> timeRanges = new ArrayList<KeyShardingRange>();
              timeRanges.add(new KeyShardingRange("_0", 20170701L, 20171231L));
              timeRanges.add(new KeyShardingRange("_1", 20180101L, 20180630L));
              timeRanges.add(new KeyShardingRange("_2", 20180701L, 20181231L));
              timeRanges.add(new KeyShardingRange("_3", 20190101L, 20190630L));
              timeRanges.add(new KeyShardingRange("_4", 20190701L, 20191231L));
              keyRangeMap.put(SHARDING_DATE_KEY, timeRanges);
          }
       
          public static Map<String, List<KeyShardingRange>> getKeyRangeMap() {
              return keyRangeMap;
          }
      }

      核心邏輯解析

      梳理一下邏輯,首先介紹一下該方法的入?yún)?/p>

      參數(shù)名                                         解釋

      availableTargetNames     有效的物理數(shù)據(jù)源,即配置文件中的 t_order_0,t_order_1,t_order_2,t_order_3

      shardingValues             分片屬性,如:{“columnName”:”order_id”,”logicTableName”:”t_order”,”values”:[“UD020003011903261545436593200002”]} ,包含:分片列名,邏輯表名,當(dāng)前列的具體分片值

      該方法返回值為

      參數(shù)名                                                 解釋

      Collection<String>      分片結(jié)果,可以是目標(biāo)數(shù)據(jù)源,也可以是目標(biāo)數(shù)據(jù)表,此處為數(shù)據(jù)源

      接著回來看業(yè)務(wù)邏輯,偽代碼如下

      首先打印了一下數(shù)據(jù)源集合 availableTargetNames 以及 分片屬性 shardingValues的值,執(zhí)行測(cè)試用例后,日志輸出為:

      availableTargetNames:["t_order_0","t_order_1","t_order_2","t_order_3"],
      shardingValues:[{"columnName":"user_id","logicTableName":"t_order","values":["UD020003011903261545436593200002"]},
                      {"columnName":"order_id","logicTableName":"t_order","values":["OD000000011903261545475143200001"]}]

      從日志可以看出,我們可以在該路由方法中取到配置時(shí)的物理數(shù)據(jù)源列表,以及在運(yùn)行時(shí)獲取本次執(zhí)行時(shí)的路由屬性及其值

      完整的邏輯流程如下:

      • 定義一個(gè)集合用于放置最終匹配好的路由數(shù)據(jù)源,接著對(duì)shardingValues進(jìn)行遍歷,目的為至少命中一個(gè)路由鍵

      • 遍歷shardingValues循環(huán)體中,打印了當(dāng)前循環(huán)的shardingValue,即實(shí)際的分片鍵的數(shù)值,如:訂單號(hào)、用戶id等。通過getIndex方法,獲取該分片鍵值中包含的物理數(shù)據(jù)源索引

      • 接著遍歷數(shù)據(jù)源列表availableTargetNames,截取當(dāng)前循環(huán)對(duì)應(yīng)availableTargetName的索引值,(eg: ds0則取0,ds1則取1…以此類推)將該配置的物理數(shù)據(jù)源索引與 第2步 中解析到的數(shù)據(jù)源路由索引進(jìn)行比較,兩者相等則表名我們期望將該數(shù)據(jù)路由到該匹配到的數(shù)據(jù)源。

      • 執(zhí)行這個(gè)過程,直到匹配到一個(gè)路由鍵則停止循環(huán),之所以這么做是因?yàn)槲覀兪菑?fù)合分片,至少要匹配到一個(gè)路由規(guī)則,才能停止循環(huán),最終將路由到的物理數(shù)據(jù)源(ds0/ds1/ds2/ds3)通過add方法添加到事先定義好的集合中并返回給框架。

      • 邏輯結(jié)束。

      感謝各位的閱讀,以上就是“Sharding-Jdbc自定義復(fù)合分片的實(shí)現(xiàn)方法”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對(duì)Sharding-Jdbc自定義復(fù)合分片的實(shí)現(xiàn)方法這一問題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是億速云,小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!

      向AI問一下細(xì)節(jié)

      免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

      AI