14-ShardingSphere的分散式主鍵實現

公众号-JavaEdge發表於2024-06-05

1 ShardingSphere自動生成鍵

MySQL自增鍵、Oracle自增序列等。分片場景下問題就複雜了,不能依靠單例項上的自增鍵來實現不同資料節點之間的全域性唯一主鍵,分散式主鍵的需求應運而生。ShardingSphere 作為一款優秀分庫分表開源軟體,同樣提供分散式主鍵實現機制。

1.1 GeneratedKey

使用 ShardingSphere 提供的自動生成鍵方案時,開發過程及效果和上面描述完全一致。

ShardingSphere實現了 GeneratedKey 類:

先從 ShardingRule 找主鍵對應 Column是否已包含:

  • 是,則找到該主鍵
  • 不是,則生成新主鍵

分散式主鍵的生成看:

image-20240605101514365

GeneratedKey#generatedValues變數儲存生成的主鍵,但生成主鍵的工作轉移到 ShardingRule#generateKey,跳轉過去:

根據logicTableName找TableRule,再找其包含的 ShardingKeyGenerator,再透過 ShardingKeyGenerator#generateKey 生成主鍵。

設計模式分析

ShardingRule只是個外觀類,真正建立 ShardingKeyGenerator 的過程在 TableRule。而這裡的 ShardingKeyGenerator 顯然就是真正生成分散式主鍵入口。

1.2 ShardingKeyGenerator

public interface ShardingKeyGenerator extends TypeBasedSPI {
    
    /**
     * Generate key.
     * 
     * @return generated key
     */
    Comparable<?> generateKey();
}

TableRule一個構造器找到 ShardingKeyGenerator 建立:

ShardingKeyGeneratorServiceLoader類定義:

// 繼承了 TypeBasedSPIServiceLoader
public final class ShardingKeyGeneratorServiceLoader extends TypeBasedSPIServiceLoader<ShardingKeyGenerator> {

  static {
    // 註冊類路徑中所有的 ShardingKeyGenerator
    NewInstanceServiceLoader.register(ShardingKeyGenerator.class);
  }

  public ShardingKeyGeneratorServiceLoader() {
    super(ShardingKeyGenerator.class);
  }
}

執行完後,ShardingKeyGeneratorServiceLoader#newService基於型別引數透過 SPI 建立例項,並賦值 Properties 屬性:

繼承 TypeBasedSPIServiceLoader 建立一個新的 ServiceLoader 類,然後在其靜態方法註冊相應 SPI 實現,這是 ShardingSphere 應用微核心模式常見做法。

sharding-core-common 工程的 META-INF/services 目錄看到具體 SPI 定義:

2 ShardingSphere分散式主鍵實現

ShardingKeyGenerator 介面存在一批實現類。除前面:

  • SnowflakeShardingKeyGenerator
  • UUIDShardingKeyGenerator

還實現了:

  • LeafSegmentKeyGenerator
  • LeafSnowflakeKeyGenerator

2.1 UUIDShardingKeyGenerator

最簡單的ShardingKeyGenerator:

2.2 SnowflakeShardingKeyGenerator

ShardingSphere最大容忍的時鐘回撥毫秒數的預設0,可透過max.tolerate.time.difference.milliseconds設定。

常量定義,維護 SnowFlake 演算法中各個 bit 之間的關係

generateKey 負責生成具體ID:

綜合考慮時鐘回撥、同一ms內請求,才完成 SnowFlake 演算法具體實現。

2.3 LeafSegmentKeyGenerator 和 LeafSnowflakeKeyGenerator

實現類似SnowflakeShardingKeyGenerator的ShardingKeyGenerator困難,也屬重複造輪子。因此,儘管 ShardingSphere 4.x提供了完整實現:

但5.x移除。目前,ShardingSphere 專門提供 OpenSharding 庫存放新版的 LeafSegmentKeyGenerator 和 LeafSnowflakeKeyGenerator。新版實現類直接採用第三方美團提供的 Leaf 開源實現。

Leaf 提供兩種生成 ID 方式:

  • 號段(Segment)模式
  • Snowflake 模式

無論哪種,都要提供一個 leaf.properties 檔案,並設定配置項。無論哪種,應用程式都要設定一個leaf.key:

# for keyGenerator key
leaf.key=sstest

# for LeafSnowflake
leaf.zk.list=localhost:2181

如用號段模式,需依賴一張資料庫表儲存執行時資料,因此要在 leaf.properties 檔案中新增資料庫配置:

# for LeafSegment
leaf.jdbc.url=jdbc:mysql://127.0.0.1:3306/test?serverTimezone=UTC&useSSL=false
leaf.jdbc.username=root
leaf.jdbc.password=123456

即可建立對應DataSource,並進一步建立用於生成分散式 ID 的 IDGen 實現類。

LeafSegmentKeyGenerator

基於號段模式的 SegmentIDGenImpl 實現類:

//透過DruidDataSource構建資料來源並設定屬性
DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUrl(properties.getProperty(LeafPropertiesConstant.LEAF_JDBC_URL));
        dataSource.setUsername(properties.getProperty(LeafPropertiesConstant.LEAF_JDBC_USERNAME));
        dataSource.setPassword(properties.getProperty(LeafPropertiesConstant.LEAF_JDBC_PASSWORD));
dataSource.init();
        
//構建資料庫訪問Dao元件
IDAllocDao dao = new IDAllocDaoImpl(dataSource);
//建立IDGen實現類
this.idGen = new SegmentIDGenImpl();
//將Dao元件繫結到IDGen實現類
((SegmentIDGenImpl) this.idGen).setDao(dao);
this.idGen.init();
this.dataSource = dataSource;

建立IDGen實現類,即可透過該類生成目標 ID,LeafSegmentKeyGenerator 類中包含所有的實現細節:

Result result = this.idGen.get(properties.getProperty(LeafPropertiesConstant.LEAF_KEY));
return result.getId();

LeafSnowflakeKeyGenerator

LeafSnowflakeKeyGenerator實現依賴於分散式協調框架 Zookeeper,所以在配置檔案中需要指定 Zookeeper 的目標地址:

# for LeafSnowflake
leaf.zk.list=localhost:2181

建立用於 LeafSnowflake 的 IDGen 實現類 SnowflakeIDGenImpl 相對比較簡單,直接在構造器設定 zk 地址即可:

IDGen idGen = new SnowflakeIDGenImpl(properties.getProperty(LeafPropertiesConstant.LEAF_ZK_LIST), 8089);

透過 IDGen 獲取模板 ID 的方式一致:

idGen.get(properties.getProperty(LeafPropertiesConstant.LEAF_KEY)).getId();

基於 Leaf 框架實現號段模式和 Snowflake 模式下的分散式 ID 生成方式非常簡單,Leaf 框架為我們遮蔽了內部實現複雜性。

3 總結

ShardingSphere的分散式主鍵設計非常獨立,本文各種分散式主鍵實現完全可直接套用到日常開發。

無論ShardingSphere自身實現的SnowflakeShardingKeyGenerator,還是基於第三方框架實現的 LeafSegmentKeyGenerator 和 LeafSnowflakeKeyGenerator,都為我們使用分散式主鍵提供直接解決方案。

參考:

  • 分散式主鍵

關注我,緊跟本系列專欄文章,咱們下篇再續!

作者簡介:魔都技術專家,多家大廠後端一線研發經驗,在分散式系統、和大資料平臺設計等方面有多年研究和實踐經驗,擁有從0到1的大資料平臺和基礎架構研發經驗,對分散式儲存、資料平臺架構、資料倉儲等領域都有豐富實踐經驗。

各大技術社群頭部專家博主。具有豐富的引領團隊經驗,深厚業務架構和解決方案的積累。

負責:

  • 中央/分銷預訂系統效能最佳化
  • 活動&優惠券等營銷中臺建設
  • 交易平臺及資料中臺等架構和開發設計
  • 車聯網核心平臺-物聯網連線平臺、大資料平臺架構設計及最佳化

目前主攻降低軟體複雜性設計、構建高可用系統方向。

參考:

  • 程式設計嚴選網

本文由部落格一文多發平臺 OpenWrite 釋出!

相關文章