方案汇总:

  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作区分) ③本地生成,不依赖db,中间件,高性能。

    缺点:

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

    ② 天然并发限制。
    Snowflake 实现的难点?

  6. 百度UidGenerator

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

 默认实现DefaultUidGenerator

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

 

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

CachedUidGenerator-buffer初始化

CachedUidGenerator-buffer填充uid 

 

  

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

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

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

 
7. 美团leaf-snowflake

完全沿用 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}。

优点:
缺点: 

posted on 2021-07-31 21:18  vow007  阅读(2)  评论(0编辑  收藏  举报  来源