分布式下的 ID 实现

分布式 —— 全局唯一 ID 实现方案

为什么需要全局唯一 ID?分布式服务架构模式下分库分表的设计,使得多个库或多个表存储相同的业务数据。这种情况根据数据库的自增ID就会产生相同ID的情况,不能保证主键的唯一性

业务系统对 ID 的要求有哪些呢?

  1. 全局唯一性:不能出现重复的ID号,既然是唯一标识,这是最基本的要求
  2. 趋势递增:在 MySQL InnoDB 引擎中使用的是聚集索引,由于多数 RDBMS 使用 B-tree 的数据结构来存储索引数据,在主键的选择上面我们应该尽量使用有序的主键保证写入性能
  3. 单调递增:保证下一个 ID 一定大于上一个 ID,例如事务版本号、IM增量消息、排序等特殊需求
  4. 信息安全:如果 ID 是连续的,恶意用户的扒取工作就非常容易做了,直接按照顺序下载指定URL即可;如果是订单号就更危险了,竞对可以直接知道我们一天的单量。所以在一些应用场景下,会需要 ID 无规则、不规则
  5. 高可用、高性能:如果 ID 生成系统不可用,则所有下游服务都不可用

对于 3、4 而言,需求是互斥的,无法使用同一个方案解决

通常使用号段模式满足 3,雪花算法满足 4

前后端联调的坑(long 传给前端接受的坑):JS Number 采用的 IEEE 754 规范,也就是相当于 Java 的 double;double 最大允许的正整数为 2 53 − 1 2^{53} - 1 2531,long 最大值为 2 63 − 1 2^{63} - 1 2631,所以可能造成数据精度的丢失

号段模式

号段模式:一次性向数据库申请一批连续的 ID 放入 JVM 缓存,当用完时再从数据库中拉取

号段模式的优点:本机单调递增、全局趋势递增

号段模式的问题:

  1. 步长的选取。步长太长,服务重启则整个号段作废;步长太短,频繁访问数据库,数据库压力很大
  2. 并发问题:多服务器同时获取号段,可能分配同一号段
  3. 线程阻塞:当号段消费完时,需要向数据库拉取号段,在此期间服务不可用
  4. 单点故障:号段拉取完全依赖于数据库,当数据库不可用时整个服务不可用

上诉问题优化:

  1. 自适应步长,步长初始值很小,当成功消费完所有补偿后还没达到阈值则加倍拉取,若中途服务重启,则将步长设为初始值
  2. 加锁。悲观锁(数据库行锁),乐观锁(版本号)
  3. 双号段缓存。在号段快用完时,提前异步加载下一个号段
  4. 多数据库实例支持:当其中一个库不可用时,使用另外一个库拉取 id

多实例支持将引入新的问题,可能造成拉取重复 id,如何避免?

引入内步长的概念。如有三台数据库实例,则将内步长置为 3。起始序列号设 A 为 1,B 为 2,C 为 3

则每次向不同实例拉取时,id + 内步长

此时 A 的 增长趋势为 1 4 7 10 …

此时 B 的 增长趋势为 2 5 8 11 …

此时 C 的 增长趋势为 3 6 9 12 …

号段模式业界经典实现:美团 Leaf-segment,滴滴 tinyid

雪花算法

雪花算法组成:1 + 41 + 10 + 12;1 是符号位始终为 0 保证 id 为正数;41 是时间戳具体到毫秒(从 1970 年开始)理论上限为 69 年;10 是所允许机器 ID 数,最大允许 1024 台机器;12 是同一时间点上最大允许生成的 id 数,即每毫秒最多分配 4096 个 ID

雪花算法优化:时间戳从 1970 年开始,白白浪费几十年,可以从实际投入使用时间开始,如 2022 年;雪花算法各个组成都可以按需进行分配

雪花算法的优点:趋势递增,不依赖第三方组件,按需分配 bit

为什么使用雪花算法,而不是使用号段模式?出于安全性考虑,如:通过 id 猜测订单数量。严禁 id 号生成单调递增

为什么使用雪花算法,而不是使用 UUID?出于性能考虑,插入数据库时主键乱序则可能导致页分裂,严重影响插入效率

雪花算法的缺陷:时间回拨;因为雪花算法生成的 ID 是基于时间的,现实世界的时间是单调的,但对于计算机而言却不是

时间回拨:服务器时间突然倒退到之前的时间,则可能产生重复 ID

造成时间回拨的原因:1. 手动改服务器时间(一般不会);2. 服务器开启 NTP 服务做时间校准(重点)

什么是 ntp?网络时间协议,用以同步分布式场景下各服务器时间;基于 UDP 协议

为什么需要 ntp,而不是使用服务器自己的时间?长时间允许的服务器会造成不可容忍的误差,所以需要同步时钟

解决时间回拨方案:等待时钟同步,如果当前生成的 ID 时间戳小于前一个就不是有效的 ID,需要循环生成直到为有效 ID;此法可能造成长时间的空转,所以需要设置最大容忍阈值

雪环算法经典业界实现:MongoDB ObjectID,美团 Leaf-snowflake,百度 UidGenerator

参考资料

  • Leaf —— 美团点评分布式ID生成系统

  • Leaf:美团分布式ID生成服务开源

  • Tinyid 原理介绍

  • UidGenerator - README

  • 分布式系统 - 全局唯一ID实现方案

你可能感兴趣的:(分布式,分布式,java,开发语言)