分布式唯一ID设计方案

方案汇总:

分布式唯一ID设计方案

  1.  UUID:
    结合机器的网卡(基于名字空间/名字的散列值MD5/SHA1)、当地时间(基于时间戳&时钟序列)、一个随记数来生成UUID。结构: aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee
    优点:结合机器的网卡(基于名字空间/名字的散列值MD5/SHA1)、当地时间(基于时间戳&时钟序列)、一个随记数来生成UUID。结构: aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee
    缺点:

    ① UUID太长。

    ② 信息不安全:基于MAC地址生成UUID的算法可能会造成MAC地址泄露。

    ③ 无序查询效率低:由于生成的UUID是无序不可读的字符串,所以其查询效率低
  2. 数据库自增
    使用数据库的id自增策略,数据库进行水平拆分,每个数据库设置不同的初始值和相同的自增步长
    优点:
    使用数据库的id自增策略,数据库进行水平拆分,每个数据库设置不同的初始值和相同的自增步长
    缺点:

    ① 方案实现复杂

    ②扩容复杂

    ③高可用问题,数据库故障后不可使用。

    ④ 存在数据泄露风险
  3. 号段模式
    每次获取一个segment(step决定大小)号段的值。用完之后再去数据库获取新的号段,可以大大的减轻数据库的压力
    优点:
    每次获取一个segment(step决定大小)号段的值。用完之后再去数据库获取新的号段,可以大大的减轻数据库的压力
    缺点:

    ID号码不够随机,能够泄露发号数量的信息,不太安全

    TP999数据波动大

    DB宕机会造成整个系统不可用

  4. Redis
    Redis的所有命令操作都是单线程的,本身提供像 incr 和 increby 这样的自增原子命令,所以能保证生成的 ID 肯定是唯一有序的
    优点:

    ① 不依赖于数据库,灵活方便,且性能优于数据库。

    ② 数字ID天然排序,对分页或者需要排序的结果很有帮助。

    缺点:

    ① 如果系统中没有Redis,还需要引入新的组件,增加系统复杂度。

    ② 需要编码和配置的工作量比较大。

  5. 雪花算法
    分布式唯一ID设计方案

    优点:

    ①整体上按照时间按时间趋势递增。

    ②全局唯一(由数据中心标识ID、机器标识ID作区分) ③本地生成,不依赖db,中间件,高性能。

    缺点:

    ①强依赖于时间,发生时钟回拨,可能会引起ID重复。

    ② 天然并发限制。
    Snowflake 实现的难点?
    分布式唯一ID设计方案分布式唯一ID设计方案
    分布式唯一ID设计方案

  6. 百度UidGenerator
    分布式唯一ID设计方案

分布式唯一ID设计方案

分布式唯一ID设计方案

分布式唯一ID设计方案

1. 实例启动的时候,往这个表中插入一行数据,得到的 id 值就是 workerId 的值 2. 扩展: 实现 WorkerIdAssigner.java 接口 ,自定义 workerID 的生成

 默认实现DefaultUidGenerator

• 时钟回拨抛异常 • • 天然的并发限制没有解决,同一秒内超出最大序列,等待下一秒

 分布式唯一ID设计方案

分布式唯一ID设计方案

    时钟回拨问题及并发限制问题解决

分布式唯一ID设计方案

CachedUidGenerator-buffer初始化分布式唯一ID设计方案

分布式唯一ID设计方案

CachedUidGenerator-buffer填充uid 

 分布式唯一ID设计方案

分布式唯一ID设计方案  

分布式唯一ID设计方案 •workerId:UidGenerator的workerId在实例每次重启时初始化,且就是数据库的自增ID,从而完美的实现每个实例获取到的workerId不会有任何冲突。解决了服务重启大步长时钟回拨引发的问题。

天然并发限制:UidGenerator不再在每次取ID时都实时计算分布式ID,而是利用RingBuffer数据结构预先生成若干个分布式ID并保存。QPS可达600万。

• • 时间递增 : 传统的雪花算法实现都是通过 System.currentTimeMillis () 来获取时间并与上一次时间进行比较,这样的实现严重依赖服务器的时间。而 UidGenerator 的时间类型是 AtomicLong , 且通过 incrementAndGet () 方法获取下一次的时间,从而脱离了对服务器时间的依赖,也就不会有时钟回拨的问题。解决了程序运行时的时钟回拨问题。

 
7. 美团leaf-snowflake

分布式唯一ID设计方案

• 完全沿用 snowflake 方案的 bit 位设计 • • 使用 Zookeeper 持久顺序节点的特性自动对 snowflake 节点配置 wokerID • • zk 持久顺序 节点: ip:port-000000001 value: {"ip":"192.168.1.1","port":"80","timestamp":1624428405410}

       Leaf-snowflake启动步骤 

• 启动 Leaf-snowflake 服务,连接 Zookeeper , 在 leaf_forever 父节点下检查自己是否已经注册过(是否有该顺序子节点)。 • 如果有注册过直接取回自己的 workerID ( zk 顺序节点生成的 int 类型 ID 号),启动服务。 • 如果没有注册过,就在该父节点下面创建一个持久顺序节点,创建成功后取回顺序号当做自己的 workerID 号,启动服务。
弱依赖 ZooKeeper
除了每次会去ZK 拿数据以外,也会在本机文件系统上缓存一个 workerID 文件。当 ZooKeeper 出现问题,恰好机器出现问题需要重启时,能保证服务能够正常启动。这样做到了对三方组件的弱依赖

 
        时钟回拨
     

1. 若写过,则用自身系统时间与 leaf_forever /${self} 节点记录时间做比较,若小于 leaf_forever /${self} 时间则认为机器时间发生了大步长回拨,服务启动失败并报警。 ● 2. 若未写过,证明是新服务节点,直接创建持久节点 leaf_forever /${self} 并写入自身系统时间,接下来综合对比其余 Leaf 节点的系统时间来判断自身系统时间是否准确,具体做法是取 leaf_temporary 下的所有临时节点 ( 所有运行中的 Leaf-snowflake 节点 ) 的服务 IP : Port , 然后通过 RPC 请求得到所有节点的系统时间,计算 sum(time)/ nodeSize 。 ● 3. 若 abs( 系统时间 - sum(time)/ nodeSize ) < 阈值,认为当前系统时间准确,正常启动服务,同时写临时节点 leaf_temporary /${self} 维持租约。 ● 4. 否则认为本机系统时间发生大步长偏移,启动失败并报警。 ● 5. 每隔一段时间 (3 s) 上报自身系统时间写入 leaf_forever/${self}。

分布式唯一ID设计方案

优点:
缺点: 

  • workerId不能重复使用,可能会超过1024
  • 时钟回拨问题只是提供了方案,代码层面并未提供
    zk,成本较高
  • 存在天然并发限制
  • 需要独立部署,依赖zk,成本较高
    ​​​​​​​

    8、其他
    ​​​​​​​思路: • ip 累加、 workerId 重复后、获取随机 workerId • 依赖jimdb

    bit 位设计
  • 沿用snowflake方案bit位设计
    1+41+10+12
    分布式唯一ID设计方案

     

    参考文献:

    https://tech.meituan.com/2017/04/21/mt-leaf.html

    https://github.com/Meituan-Dianping/Leaf

    https://github.com/Meituan-Dianping/Leaf/pull/94

    https://github.com/baidu/uid-generator

 

上一篇:Centos7配置SVN服务端


下一篇:project1