暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Hubble技术栈-分布式ID篇

Hubble技术架构 2021-04-18
1491

一、概述

    随着应用的微服务化改造、数据分库分表、数据量越来越大,传统的DB自增ID难以满足大型分布式应用的需求,那么本篇来阐述分布式全局唯一ID的理论和实践。

    实践中,业务对分布式ID规则有哪些要求?对分布式ID系统有什么要求?分布式ID算法有哪些?业内大厂是怎么用的?我们又是怎么玩的?接下来一一揭晓。


二、分布式序列要求

    分布式序列是一个通用的产品,要满足以要求:

2.1、全局唯一性

    在同一场景下不能出现重复的ID号,既然是唯一标识,这是最基本的要求。


2.2趋势递增

    在主键的选择上面我们应该尽量使用有序的主键保证写入性能、同时有利于索引提高查询的性能。


2.3序列数据类型

    序列的数据类型决定的存储空间大小,序列尽可能用整类型,以MYSQL bigint unsigned为例存储空间为8字节,范围(0~2*2^63-1 )是一个极大的整数,足以满足需求。


2.4简单易用

    能够拿来即用,接入方便,同时在系统设计和实现上要尽可能的简单。


三、序列常用算法

3.1、UUID

    UUID 含义是通用唯一识别码 (Universally Unique Identifier),这是一个软件建构的标准。UUID是由一组32位数的16进制数字所构成,UUID理论上的总数为16^32=2^128。若每纳秒产生1兆个UUID,要花100亿年才会将所有UUID用完。


UUID组成:

标准的UUID格式为:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx (8-4-4-4-12)。

优点:

    1. 本地生成,无依赖,速度极快,序号个数无限。

    2. 序号全局唯一性。


不足:

     1. 序列无规律,对索引不友好。

     2. 字段串由数字与字母组合,存储空间大。


3.2、雪化算法(snowflake)

    snowflake是Twitter开源的分布式ID生成算法,支持多语言(Java、go、scala等),结果是一个64bit大小的整型的ID。结构图如下:


位数说明:

标识
位数
说明
序列号12bit毫秒内的计数,12位的计数顺序号最大支持每个节点每毫秒产生4096个ID序号。
数据机器位10bit最大支持1024个节点,包括5位datacenterId和5位workerId。
时间戳
41bit存储时间截的差值,41位的时间戳,毫秒级,可以使用69年。
标识1bit正数是0,负数是1,ID一般是正数,最高位是0。

优点:

    1. 本地生成,无依赖。

    2. 整型,趋势递增,有效利于索引。

    3. 效率较高,理论每节点每秒最大能产生26万ID。

不足:

    1. 依赖机器时间,如果发生时钟回拨会导致可能生成id重复。

    2. workid节点有限,无存储容易碰撞,在超大服务集群使用有风险。


3.3、基于DB数据段算法

    利用DB表的存储机制,表主要字段有:命名空间、当前最大值、步长、版本号。通过步长的大小及利用缓存数据段来控制,解决DB的性能瓶颈,提高效率。

数据库表主要字段:

字段
字段名称
说明
version
版本号用于防并发的乐观锁。
value
当前最大值当前namespace业务场景的最大值。
step步长每次从数据库获取序号段到服务节点内存,步长越大对DB的压力越小。
retrytimes重试次数乐观锁更新异常时重试次数。
namespace命名空间业务场景,用于区分不同的业务。业务之间相互隔间,互不影响。

优点:

    1. 高性能,序列无极限(取决于步长长度)。

    2. 整型,有顺序,趋势递增,有利于索引。

    3. 可以自定义value的大小,非常方便业务从原有的ID方式迁移过来。


不足:

    1. 服务强依赖DB,来存储业务场景、最大值、步长等信息。

    2. 中心化部署,序列号段缓存服务节点,对序列服务可靠性要求极高。


四、业内大厂序列方案

4.1、美团点评(Leaf)

    Leaf美团点评分布式ID生成系统,leaf是基于springboot、以HTTP协议的方式提供获取分布式唯一ID的服务。总计集成了两种模式:Leaf-snowflake算法和Leaf-segment(基于DB数据段算法)。

    Leaf-segment在基于DB数据段算法上进行了优化:在号段消耗的临界点的ID时,会用异步线程从DB获取新的号段,补充进行到内存中,避免号段消耗再补充新的号段。

    Leaf-snowflake在原生snowflake进行了优化,弱依赖Zookeeper及延迟3秒上报时间差,可以缓解时间回拔的问题。

      

优点:

    1. Leaf-segment 双buffer优化,避免号段消耗完,再更新DB的TP999波动。

    2. Leaf-snowflake 缓解时间回拔的问题。


不足:

    1. 中心化的部署,通过http获取序列,存在网络开销。

    2. Leaf-server故障会影响所有序列的生成,必须保证Leaf-server可用性100%。


部署方式:

    中心化部署,leaf-server是基于springboot,以HTTP协议的方式提供获取分布式唯一ID的服务。


4.2、滴滴出行(Tinyid)

    Tinyid是滴滴出行推出的分布序列生成器。Tinyid是基于美团点评分布式ID生成系统(Leaf)基础上进行了优化。


优化办法:

    1. 双号段缓存,提前异步加载下一个号段,避免号段消耗再补充新的号段。

    2. 增加tinyid-client,把号段缓存在应用的客户端,有效的减少Tinyid-server的网格开销。


最终架构图如下:

优点:

    1. Tinyid提供http和tinyid-client两种方式接入。

    2. tinyid-client有效的减少的网格开销,大大提高了性能。

    3. tinyid-server容忍短时间的down掉,大大的提高了可用性。


不足:

    暂无


部署方式:

    中心化部署,tinyid-server是基于springboot,以HTTP协议及tinyid-client两种方式提供获取分布式唯一ID的服务。


4.3、百度(UidGenerator)

    UidGenerator是百度开源的Java语言实现,基于雪花算法的唯一ID生成器。对Snowflake算法的组成部分进行了调整、优化和改进,并解决workid不足及时钟回拨序列重复的问题。它通过消费未来时间克服了雪花算法的并发限制,UidGenerator提前生成ID并压测结果显示,单独实例的QPS理论最大值超过600W。

 

Uid结构图:


位数说明

标识
位置
说明
标识1bit正数是0,负数是1,ID一般是正数,最高位是0。
时间截
28bit存储时间截的差值,28位的时间截,可以使用8.5年(2^28-1/86400/3655)。
数据机器位22bit最大支持400W个节点。
序列号13bit毫秒内的计数,13位的计数顺序号最大支持每个节点每毫秒产生8192个ID序号。

默认调整了各段的位数。


优点:

    1. QPS最大值突破生原nowflake算法的限制。

    2. 解决了时钟回拨序列重复的问题。

    3. 通过依赖DB优化workid顺序问题,并提升了节点数。

    4. snowflake结构图可以自定义调整。


不足:

    1. 在原生Snowflake算法新增了DB的依赖。

   2. 因调整时间部分位数,Uid寿命减短,只能承受8.5年(2 ^ 28-1 86400/365)。

    3. 中心化的部署,通过HTTP获取序列,存在网络开销。

   4. uid-server故障会影响所有序列的生成,必须保证uid-server可用性100%。


部署方式:

    中心化部署,uid-server是基于springboot,以HTTP协议的方式提供获取分布式唯一ID的服务。


4.4、阿里(TDDL-sequence)

    TDDL(Taobao Distributed Data Layer)是阿里的一个分库分表数据库中间件,它在阿里内部被广泛的使用,主要是为了解决分布式数据库产生的相关问题。而tddl-sequence 顾名思义,为解决分布式序列而生的组件。

    tddl-sequence的原理基于DB数据段算法,以客户端JAR方式的与应用共存,在应用的DB中创建序列表,并利用应用的数据源,在本地生成序列。这种去中心化部署,减少获取序列http接口的网络开销,也不担心seq-server故障的带来的风险。

tddl-sequence架构图:

优点:

    1. 去中心化部署,本地获取序列,本地缓存号段,极大提高序列的性能。

    2. 不同应用之间的序列生成互不影响,极大提高容错性了。


不足:

    1. 接入的复杂度增加,每个应用都要创建自己的序列表。


部署方式:

    分布式部署方式,以JAR的方式与应用有机的结合在一起。


五、序列部署方式(中心化 & 分布式)

    在前面介绍的各大厂的分布式序列的方案中,部署方式分为:中心化、分布式两方式,两者之间对序列的性能、可用性、数据一致要求是不一样的。


中心化部署:

    中心化部署是指:序列以微服务的方式单部署,连接独立的序列库。提供Http、RPC、restful等协议的接口,给各应用调用并返回序列或号段。


分布式部署:

    分布式部署是指:序列以组件JAR的方式,应用有机的结合一起部署,把序列的相关表,与应用的数据表放一个库,共用数据源。


两者的要求区别如下:

项目
中心化
分布式
高性能
平均延迟和TP999延迟都要尽可能低当前本地生成,无网络开销,性能高
可用性无限接近于100%的可用性,但可用性达到99.999(5个9)与当前应用的可用性一致。

六、hubble-sequence

    哈勃分布式序列(hubble-sequence) 是参考了业内大厂的几个分布式序列方案后,全面了解各方案的优缺点,并结合行内的实际情况形成的方案。最终采用非中心化部署的阿里tddl-sequence为基于DB数据段算法的原型,这命名为AI (AutoIncre简写),并集成雪化算法(snowflake)。

    Hubble-sequence哈勃序列生成工具。确保全局唯一、高性能序列的组件。内置丰富的算法,满足各种序列场景的需求。


架构图如下:

算法介绍:

算法
类型
功能描述
snowflakeLong原生的snowflake算法。
snowflake16
String

原生snowflake算法转36进制(13位)+随机转36进制(3位)

长度固定16位。

snowflake20
String
原生snowflake算法转36进制(13位)+namepace转36进制(4位)+随机转36进制(3位),长度固定20位.
ai
Long从0-Long.MAX_VALUE,最大19位数据足够大,不支持循环。
dateAI
Long格式:yyyyMMdd+序列。

11位自增序列:从00000000001-99999999999

支持循环:当序列大于99999999999时自动归位00000000001

esbAI
String

格式:系统号(6位)+yyMMdd(6位)+序列(10位)

10位自增序列:从0000000001-9999999999

支持循环:当序列大于9999999999时自动归位0000000001

timeAI
Long

格式:yyMMddHHmmss

7位自增序列:从0000001-9999999

支持循环:当序列大于9999999时自动归位0000001

优点:

    1. 非中心化部署,本地获取序列,本地缓存号段,极大提高序列的性能。

    2. AutoIncre算法不同应用之间的序列生成互不影响,极大提高容错性了。

    3. 支持halo、spring boot、pafa5架构。

    4. 扩展算法丰富,满足各种序列场景的需求。


不足:

    1. 接入复杂度增加,每个应用都要创建自己的序列表。


部署方式:

    去中心化部署方式,以jar的方式与应用有机的结合在一起。


七、总结

      分布式序列各大厂公司做法都差不多,无非都是在snowflake、基于DB数据段两大基础算法及结合自身的特点做优化。

      hubble-squence后续会增加中心化的方式来满足各种业务方的需求。


文章转载自Hubble技术架构,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论