14-ShardingSphere的分布式主键实现

1 ShardingSphere自动生成键

MySQL自增键、Oracle自增序列等。分片场景下问题就复杂了,不能依靠单实例上的自增键来实现不同数据节点之间的全局唯一主键,分布式主键的需求应运而生。ShardingSphere 作为一款优秀分库分表开源软件,同样提供分布式主键实现机制。

1.1 GeneratedKey

使用 ShardingSphere 提供的自动生成键方案时,开发过程及效果和上面描述完全一致。

ShardingSphere实现了 GeneratedKey 类:

先从 ShardingRule 找主键对应 Column是否已包含:

  • 是,则找到该主键
  • 不是,则生成新主键

分布式主键的生成看:

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 发布!

热门相关:我的绝色美女房客   黄金渔场   以权谋妻   大爱晚成,卯上天价老婆   完美再遇