其中,數據源分片策略表示:數據路由到的物理目標數據源,表分片策略表示數據被路由到的目標表。java
特別的,表分片策略是依賴於數據源分片策略的,也就是說要先分庫再分表,固然也能夠只分表。算法
Sharding-JDBC的分片策略包含了分片鍵和分片算法。因爲分片算法與業務實現緊密相關,所以Sharding-JDBC沒有提供內置的分片算法,而是經過分片策略將各類場景提煉出來,提供了高層級的抽象,經過提供接口讓開發者自行實現分片算法。sql
如下內容引用自官方文檔。官方文檔數據庫
首先介紹四種分片算法。apache
經過分片算法將數據分片,支持經過=、BETWEEN和IN分片。
分片算法須要應用方開發者自行實現,可實現的靈活度很是高。api目前提供4種分片算法。因爲分片算法和業務實現緊密相關,
所以並未提供內置分片算法,而是經過分片策略將各類場景提煉出來,
提供更高層級的抽象,並提供接口讓應用開發者自行實現分片算法。框架
用於分片的數據庫字段,是將數據庫(表)水平拆分的關鍵字段。例:將訂單表中的訂單主鍵的尾數取模分片,則訂單主鍵爲分片字段。 SQL中若是無分片字段,將執行全路由,性能較差。 除了對單分片字段的支持,ShardingSphere也支持根據多個字段進行分片。ide
經過分片算法將數據分片,支持經過=
、BETWEEN
和IN
分片。分片算法須要應用方開發者自行實現,可實現的靈活度很是高。性能
目前提供4種分片算法。因爲分片算法和業務實現緊密相關,所以並未提供內置分片算法,而是經過分片策略將各類場景提煉出來,提供更高層級的抽象,並提供接口讓應用開發者自行實現分片算法。測試
對應PreciseShardingAlgorithm,用於處理使用單一鍵做爲分片鍵的=與IN進行分片的場景。須要配合StandardShardingStrategy使用。
對應RangeShardingAlgorithm,用於處理使用單一鍵做爲分片鍵的BETWEEN AND進行分片的場景。須要配合StandardShardingStrategy使用。
對應ComplexKeysShardingAlgorithm,用於處理使用多鍵做爲分片鍵進行分片的場景,包含多個分片鍵的邏輯較複雜,須要應用開發者自行處理其中的複雜度。須要配合ComplexShardingStrategy使用。
對應HintShardingAlgorithm,用於處理使用Hint行分片的場景。須要配合HintShardingStrategy使用。
包含分片鍵和分片算法,因爲分片算法的獨立性,將其獨立抽離。真正可用於分片操做的是分片鍵 + 分片算法,也就是分片策略。目前提供5種分片策略。
對應StandardShardingStrategy。提供對SQL語句中的=, IN和BETWEEN AND的分片操做支持。StandardShardingStrategy只支持單分片鍵,提供PreciseShardingAlgorithm和RangeShardingAlgorithm兩個分片算法。PreciseShardingAlgorithm是必選的,用於處理=和IN的分片。RangeShardingAlgorithm是可選的,用於處理BETWEEN AND分片,若是不配置RangeShardingAlgorithm,SQL中的BETWEEN AND將按照全庫路由處理。
對應ComplexShardingStrategy。複合分片策略。提供對SQL語句中的=, IN和BETWEEN AND的分片操做支持。ComplexShardingStrategy支持多分片鍵,因爲多分片鍵之間的關係複雜,所以並未進行過多的封裝,而是直接將分片鍵值組合以及分片操做符透傳至分片算法,徹底由應用開發者實現,提供最大的靈活度。
對應InlineShardingStrategy。使用Groovy的表達式,提供對SQL語句中的=和IN的分片操做支持,只支持單分片鍵。對於簡單的分片算法,能夠經過簡單的配置使用,從而避免繁瑣的Java代碼開發,如: t_user_$->{u_id % 8}
表示t_user表根據u_id模8,而分紅8張表,表名稱爲t_user_0
到t_user_7
。
對應HintShardingStrategy。經過Hint而非SQL解析的方式分片的策略。
對應NoneShardingStrategy。不分片的策略。
對於分片字段非SQL決定,而由其餘外置條件決定的場景,可以使用SQL Hint靈活的注入分片字段。例:內部系統,按照員工登陸主鍵分庫,而數據庫中並沒有此字段。SQL Hint支持經過Java API和SQL註釋(待實現)兩種方式使用。
因爲目的爲貼近實戰,所以着重講解如何實現複雜分片策略,即實現ComplexShardingStrategy接口定製生產可用的分片策略。
AdminIdShardingAlgorithm 複合分片算法代碼以下:
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; } }
梳理一下邏輯,首先介紹一下該方法的入參
參數名 解釋
availableTargetNames 有效的物理數據源,即配置文件中的 t_order_0,t_order_1,t_order_2,t_order_3
shardingValues 分片屬性,如:{「columnName」:」order_id」,」logicTableName」:」t_order」,」values」:[「UD020003011903261545436593200002」]} ,包含:分片列名,邏輯表名,當前列的具體分片值
該方法返回值爲
參數名 解釋
Collection<String> 分片結果,能夠是目標數據源,也能夠是目標數據表,此處爲數據源
接着回來看業務邏輯,僞代碼以下
首先打印了一下數據源集合 availableTargetNames 以及 分片屬性 shardingValues的值,執行測試用例後,日誌輸出爲:
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"]}]
從日誌能夠看出,咱們能夠在該路由方法中取到配置時的物理數據源列表,以及在運行時獲取本次執行時的路由屬性及其值
完整的邏輯流程以下:
本文中,基本完成了Sharding-JDBC中複合分片路由算法的自定義實現,並通過測試驗證符合預期,該實現方案在生產上已經經歷過考驗。定義分片路由策略的核心仍是要熟悉ComplexKeysShardingAlgorithm,對如何解析 doSharding(Collection availableTargetNames, CollectionshardingValues)的參數有明確的認識,最簡單的方法就是實際打印一下參數,相信會讓你更加直觀的感覺到做者優良的接口設計能力,站在巨人的肩膀上咱們能看到更遠。