Sharding-JDBC分片策略、分佈式事務、全局ID

分片策略詳解 :

  https://shardingsphere.apache.org/document/current/cn/features/sharding/concept/sharding/

  Sharding-JDBC 中的分片策略有兩個維度:分庫(數據源分片)策略和分表策略。

  分庫策略表示數據路由到的物理目標數據源,分表分片策略表示數據被路由到的目標表。分表策略是依賴於分庫策略的,也就是說要先分庫再分表,當然也可以不分庫只分表。

  跟 Mycat 不一樣,Sharding-JDBC 沒有提供內置的分片算法,而是通過抽象成接口,讓開發者自行實現,這樣可以根據業務實際情況靈活地實現分片。

分片策略:

  包含分片鍵和分片算法,由於分片算法的獨立性,將其獨立抽離。真正可用於分片操作的是分片鍵 + 分片算法,也就是分片策略。分片算法是需要自定義的。可以用於分庫,也可以用於分

  Sharding-JDBC 提供了 5 種分片策略,這些策略全部繼承自 ShardingStrategy。

  1. 標準分片策略:對應 StandardShardingStrategy。提供對 SQL語句中的 =><>=<=IN 和 BETWEEN AND 的分片操作支持。 StandardShardingStrategy 只支持單分片鍵,提供 PreciseShardingAlgorithm 和 RangeShardingAlgorithm 兩個分片算法。 PreciseShardingAlgorithm 是必選的,用於處理 = 和 IN 的分片。 RangeShardingAlgorithm 是可選的,用於處理 BETWEEN AND><>=<=分片,如果不配置 RangeShardingAlgorithm,SQL 中的 BETWEEN AND 將按照全庫路由處理。
  2. 複合分片策略:對應 ComplexShardingStrategy。複合分片策略。提供對 SQL 語句中的 =><>=<=IN 和 BETWEEN AND 的分片操作支持。 ComplexShardingStrategy 支持多分片鍵,由於多分片鍵之間的關係複雜,因此並未進行過多的封裝,而是直接將分片鍵值組合以及分片操作符透傳至分片算法,完全由應用開發者實現,提供最大的靈活度。
  3. 行表達式分片策略:對應 InlineShardingStrategy。使用 Groovy 的表達式,提供對 SQL 語句中的 = 和 IN 的分片操作支持,只支持單分片鍵。 對於簡單的分片算法,可以通過簡單的配置使用,從而避免繁瑣的 Java 代碼開發,如: t_user_$->{u_id % 8} 表示 t_user 表根據 u_id 模 8,而分成 8 張表,表名稱爲 t_user_0 到 t_user_7。 
  4. Hint分片策略:對應 HintShardingStrategy。通過 Hint 指定分片值而非從 SQL 中提取分片值的方式進行分片的策略。
  5. 不分片策略:對應 NoneShardingStrategy。不分片的策略。

分片算法:

  通過分片算法將數據分片,支持通過 =>=<=><BETWEEN 和 IN 分片。 分片算法需要應用方開發者自行實現,可實現的靈活度非常高。目前提供4種分片算法。 由於分片算法和業務實現緊密相關,因此並未提供內置分片算法,而是通過分片策略將各種場景提煉出來,提供更高層級的抽象,並提供接口讓應用開發者自行實現分片算法。

  1. 精確分片算法:對應 PreciseShardingAlgorithm,用於處理使用單一鍵作爲分片鍵的 = 與 IN 進行分片的場景。需要配合 StandardShardingStrategy 使用。
  2. 範圍分片算法:對應 RangeShardingAlgorithm,用於處理使用單一鍵作爲分片鍵的 BETWEEN AND><>=<=進行分片的場景。需要配合 StandardShardingStrategy 使用。
  3. 複合分片算法:對應 ComplexKeysShardingAlgorithm,用於處理使用多鍵作爲分片鍵進行分片的場景,包含多個分片鍵的邏輯較複雜,需要應用開發者自行處理其中的複雜度。需要配合 ComplexShardingStrategy 使用。
  4. Hint分片算法:對應 HintShardingAlgorithm,用於處理使用 Hint 行分片的場景。需要配合 HintShardingStrategy 使用。

  算法實現 :

  實現的話就是實現對應的算法接口,並且把實現類配置進去即可。下列就是這四種算法對應的接口。實現他們的  doSharding 方法即可。

public interface PreciseShardingAlgorithm<T extends Comparable<?>> extends ShardingAlgorithm {
    
    String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<T> shardingValue);
}

public interface RangeShardingAlgorithm<T extends Comparable<?>> extends ShardingAlgorithm {
    
    Collection<String> doSharding(Collection<String> availableTargetNames, RangeShardingValue<T> shardingValue);
}

public interface ComplexKeysShardingAlgorithm extends ShardingAlgorithm {
    Collection<String> doSharding(Collection<String> availableTargetNames, Collection<ShardingValue> shardingValues);
}

public interface HintShardingAlgorithm extends ShardingAlgorithm {
    Collection<String> doSharding(Collection<String> availableTargetNames, ShardingValue shardingValue);
}

  然後配置分庫分表策略的時候進行注入就行:

userTableRuleConfig.setDatabaseShardingStrategyConfig(new StandardShardingStrategyConfiguration("user_id", DBShardAlgo.class.getName()));
userTableRuleConfig.setTableShardingStrategyConfig(new StandardShardingStrategyConfiguration("user_id",TblPreShardAlgo.class.getName(), TblRangeShardAlgo.class.getName()));

  其中 DBShardAlgo、TblPreShardAlgo、TblRangeShardAlgo都是自定義的策略類。如果 springboot下面 ,則按照如下配置:

sharding.jdbc.config.sharding.tables.user_info.databaseStrategy.standard.shardingColumn=user_id
sharding.jdbc.config.sharding.tables.user_info.databaseStrategy.standard.preciseAlgorithmClassName=com.wuzz.demo.config.DBShardAlgo
sharding.jdbc.config.sharding.tables.user_info.tableStrategy.standard.shardingColumn=user_id
sharding.jdbc.config.sharding.tables.user_info.tableStrategy.standard.preciseAlgorithmClassName=com.wuzz.demo.config.TblPreShardAlgo
sharding.jdbc.config.sharding.tables.user_info.tableStrategy.standard.rangeAlgorithmClassName=com.wuzz.demo.config.TblRangeShardAlgo

分佈式事務支持:

  官網:https://shardingsphere.apache.org/document/current/cn/features/transaction/

  本地事務:

  在不開啓任何分佈式事務管理器的前提下,讓每個數據節點各自管理自己的事務。 它們之間沒有協調以及通信的能力,也並不互相知曉其他數據節點事務的成功與否。 本地事務在性能方面無任何損耗,但在強一致性以及最終一致性方面則力不從心。

  兩階段提交:

  XA協議最早的分佈式事務模型是由 X/Open 國際聯盟提出的 X/Open Distributed Transaction Processing (DTP) 模型,簡稱 XA 協議。

  基於XA協議實現的分佈式事務對業務侵入很小。 它最大的優勢就是對使用方透明,用戶可以像使用本地事務一樣使用基於XA協議的分佈式事務。 XA協議能夠嚴格保障事務 ACID 特性。

  嚴格保障事務 ACID 特性是一把雙刃劍。 事務執行在過程中需要將所需資源全部鎖定,它更加適用於執行時間確定的短事務。 對於長事務來說,整個事務進行期間對數據的獨佔,將導致對熱點數據依賴的業務系統併發性能衰退明顯。 因此,在高併發的性能至上場景中,基於XA協議的分佈式事務並不是最佳選擇。

  柔性事務:

  如果將實現了 ACID 的事務要素的事務稱爲剛性事務的話,那麼基於 BASE 事務要素的事務則稱爲柔性事務。 BASE 是基本可用、柔性狀態和最終一致性這三個要素的縮寫。

  • 基本可用(Basically Available)保證分佈式事務參與方不一定同時在線。

  • 柔性狀態(Soft state)則允許系統狀態更新有一定的延時,這個延時對客戶來說不一定能夠察覺。

  • 而最終一致性(Eventually consistent)通常是通過消息傳遞的方式保證系統的最終一致性。

  在 ACID 事務中對隔離性的要求很高,在事務執行過程中,必須將所有的資源鎖定。 柔性事務的理念則是通過業務邏輯將互斥鎖操作從資源層面上移至業務層面。通過放寬對強一致性要求,來換取系統吞吐量的提升。

  基於 ACID 的強一致性事務和基於 BASE 的最終一致性事務都不是銀彈,只有在最適合的場景中才能發揮它們的最大長處。 可通過下表詳細對比它們之間的區別,以幫助開發者進行技術選型。

  Sharding-JDBC下實現強一致分佈式事務需要導入以下依賴:

<!--xa分佈式事務-->
<dependency>
  <groupId>io.shardingsphere</groupId>
  <artifactId>sharding-transaction-2pc-xa</artifactId>
  <version>3.1.0</version>
</dependency>

<dependency>
  <groupId>io.shardingsphere</groupId>
  <artifactId>sharding-transaction-spring-boot-starter</artifactId>
  <version>3.1.0</version>
</dependency>

  默認是用 atomikos 實現的。在 Service 類上加上註解:

@ShardingTransactionType(TransactionType.XA)
@Transactional(rollbackFor = Exception.class)

分佈式全局 ID:

  傳統數據庫軟件開發中,主鍵自動生成技術是基本需求。而各個數據庫對於該需求也提供了相應的支持,比如 MySQL 的自增鍵,Oracle 的自增序列等。 數據分片後,不同數據節點生成全局唯一主鍵是非常棘手的問題。同一個邏輯表內的不同實際表之間的自增鍵由於無法互相感知而產生重複主鍵。 雖然可通過約束自增主鍵初始值和步長的方式避免碰撞,但需引入額外的運維規則,使解決方案缺乏完整性和可擴展性。

  目前有許多第三方解決方案可以完美解決這個問題,如 UUID 等依靠特定算法自生成不重複鍵,或者通過引入主鍵生成服務等。爲了方便用戶使用、滿足不同用戶不同使用場景的需求, Apache ShardingSphere 不僅提供了內置的分佈式主鍵生成器,例如 UUID、SNOWFLAKE,還抽離出分佈式主鍵生成器的接口 KeyGenerator,方便用戶自行實現自定義的自增主鍵生成器。

  內置的主鍵生成器:

  • UUID:採用 UUID.randomUUID() 的方式產生分佈式主鍵。
  • SNOWFLAKE:在分片規則配置模塊可配置每個表的主鍵生成策略,默認使用雪花算法(snowflake)生成 64bit 的長整型數據。雪花算法是由 Twitter 公佈的分佈式主鍵生成算法,它能夠保證不同進程主鍵的不重複性,以及相同進程主鍵的有序性。

  Java config 配置:

tableRuleConfig.setKeyGeneratorColumnName("order_id");
tableRuleConfig.setKeyGeneratorClass("io.shardingsphere.core.keygen.DefaultKeyGenerator");

  keyGeneratorColumnName:指定需要生成 ID 的列

  KeyGenerotorClass:指定生成器類,默認是 DefaultKeyGenerator.java,裏面使用了雪花算法。

  Properties 配置:

#全局默認算法
sharding.jdbc.config.sharding.default-key-generator-class-name=
#指定表
sharding.jdbc.config.sharding.tables.t_order.keyGeneratorColumnName=
sharding.jdbc.config.sharding.tables.t_order.keyGeneratorClassName=

Sharding-JDBC 工作流程:

  內核剖析https://shardingsphere.apache.org/document/current/cn/features/sharding/principle/

  Sharding-JDBC 的原理總結起來很簡單:SQL 解析 => 執行器優化 => SQL 路由 => SQL 改寫 => SQL 執行 => 結果歸併。

  1. SQL 解析:分爲詞法解析和語法解析。 先通過詞法解析器將 SQL 拆分爲一個個不可再分的單詞。再使用語法解析器對 SQL 進行理解,並最終提煉出解析上下文。 解析上下文包括表、選擇項、排序項、分組項、聚合函數、分頁信息、查詢條件以及可能需要修改的佔位符的標記。SQL 解析主要是詞法和語法的解析。目前常見的 SQL 解析器主要有 fdb,jsqlparser和 Druid。Sharding-JDBC1.4.x 之前的版本使用 Druid 作爲 SQL 解析器。從 1.5.x 版本開始,Sharding-JDBC 採用完全自研的 SQL 解析引擎。
  2. 執行器優化:合併和優化分片條件,如 OR 等。
  3. SQL 路由:SQL 路由是根據分片規則配置以及解析上下文中的分片條件,將 SQL 定位至真正的數據源。它又分爲直接路由、簡單路由和笛卡爾積路由。直接路由,使用 Hint 方式。Binding 表是指使用同樣的分片鍵和分片規則的一組表,也就是說任何情況下,Binding 表的分片結果應與主表一致。例如:order 表和 order_item 表,都根據 order_id分片,結果應是 order_1 與 order_item_1 成對出現。這樣的關聯查詢和單表查詢複雜度和性能相當。如果分片條件不是等於,而是 BETWEEN 或 IN,則路由結果不一定落入單庫(表),因此一條邏輯 SQL 最終可能拆分爲多條 SQL 語句。笛卡爾積查詢最爲複雜,因爲無法根據 Binding 關係定位分片規則的一致性,所以非Binding 表的關聯查詢需要拆解爲笛卡爾積組合執行。查詢性能較低,而且數據庫連接數較高,需謹慎使用。
  4. SQL 改寫:將邏輯表名稱改成真實表名稱,優化分頁查詢等
  5. SQL 執行:因爲可能鏈接到多個真實數據源, Sharding -JDBC 將採用多線程併發執行 SQL。
  6. 結果歸併:例如數據的組裝、分頁、排序等等。

Sharding-JDBC 實現原理:

  JDBC 的四大核心對象?DataSource、Connection、Statement(PS)、ResulstSet。

  Sharding-JDBC 封裝了這四個核心類,在類名前面加上了 Sharding。

  如果說帶 Sharding 的類要替換 JDBC 的對象,那麼一定要找到創建和調用他們的地方。ShardingDataSource 我們不說了,系統啓動的時候就創建好了。問 題 就 在 於 , 我 們 是 什 麼 時 候 用 ShardingDataSource 獲 取 一 個ShardingConnection 的?我們以整合了 MyBatis 的項目爲例。MyBatis 封裝了 JDBC 的核心對象,那麼在MyBatis 操作 JDBC 四大對象的時候,就要替換成 Sharding-JDBC 的四大對象。 我 們 的 查 詢 方 法 最 終 會 走 到SimpleExecutor 的 doQuery()方法,這個是我們的前提知識,那我們直接在 doQuery()打斷點。doQuery()方法裏面調用了 prepareStatement()創建連接。而返回的就是 一個 ShardingConnection ,所以後續都是採用 Sharding-JDBC自己的實現類去操作,從而替換了四大對象。

  更多內容請參考官網:https://shardingsphere.apache.org/document/legacy/4.x/document/cn/overview/ .可以切換成中文.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章