欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

分布式id是什么意思(六种分布式ID生成方法)

程序员文章站 2023-11-21 19:19:52
在分布式系统中,经常需要对大量的数据、消息、http请求等进行唯一标识,例如链路追踪traceid、身份标识号、订单流水号、操作记录流水号、优惠券id等等。这个时候数据库自增主键已经不能满足需求,需要...

在分布式系统中,经常需要对大量的数据、消息、http请求等进行唯一标识,例如链路追踪traceid、身份标识号、订单流水号、操作记录流水号、优惠券id等等。

这个时候数据库自增主键已经不能满足需求,需要一个能够生成分布式id的系统。

分布式id的特性

  1. 全局唯一。不能出现重复的id,这是最基本的要求。
  2. 递增。递增有利于关系数据库索引性能。除了常见的连续递增,如1001,1002,1003等等,分布式id还存在趋势递增的形式,即保证下一个id大于上一个id但不连续。这样的好处可以防止关键信息被泄露,例如toc业务中暴露给用户的id,可能会暴露用户数量。(订单id同理)
  3. 高可用。为多个服务提供id服务,一旦宕机,会造成严重影响。
  4. 好接入。秉着拿来即用的设计原则,接入文档要尽可能的简单。
  5. 高性能:必须要在压测下表现良好,如果达不到要求则在高并发环境下会导致系统瘫痪。
  6. 灵活多变:每个业务场景对id的要求也各不相同,id生成要做到灵活多变可配置,尽可能多的满足需求。

解决方案

1. uuid

uuid是universally unique identifier的缩写,包含32个16进制数字,以连字号分为五段,形式为8-4-4-4-12包含36个字符的字符串,例如:
321dsa13-das2-d231-gfdd-213as8asd899

uuid经由一定的算法机器生成,为了保证uuid的唯一性,规范定义了包括网卡mac地址、时间戳、名字空间、随机或伪随机数、时序等元素,以及从这些元素生成uuid的算法。

优点:

  1. 性能非常高,本地生成,没有网络消耗。
  2. 生成简单,没有高可用风险。
  3. 有利于信息安全,因为可读性差,无规律。

缺点:

  1. 太长,不易于存储。
  2. 无序,对mysql索引不利,在innodb中,uuid的无序性可能会引起数据位置频繁变动,严重影响性能。
  3. uuid不能标识业务含义,可读性差。

2. 数据库自增 id

利用数据库自增id的特性来生成,如mysql的auto_increment。其优点是数字类型,并且可以自增。当然缺点就是并发场景下的性能瓶颈。

优点:

  1. 简单,利用数据库自有功能实现。
  2. id严格连续自增,可以实现一些对id有特殊要求的业务。

缺点:

  1. 有重复发号的风险,例如mysql数据库主从切换的场景。
  2. 发号性能限制于数据库性能。
  3. 强依赖数据库,当数据库异常时整个系统不可用。

进一步优化:
放弃主从复制的高可用架构,采用多主架构。每个主库设置不同的起始值和相同的步长,保证了号段的隔离。

3. redis

redis中的incr命令,可以实现原子自增。相比较数据库而言,redis可支撑的并发量非常高,性能好。

但需要考虑下面两种情况造成的数据不一致问题:

  1. 宕机后重启恢复但存在未及时初始化。
  2. 主从切换,主从数据同步延迟。

优点:

  1. 简单,自有能力。
  2. 高并发环境下性能好,优于数据库。

缺点:

  1. 可能会重复发号。
  2. 需要保障redis服务的高可用。

4. zookeeper 实现

使用zookeeper作为分段节点协调工具,每台服务器首先从zookeeper 获取一段号码,如[1,1000]的id,此时zookeeper上保存最大值 1000,每次获取的时候都会进行判断,如果id <=1000,则更新本地的当前值,如果为1001,则会将zookeeper 上的最大值更新至2000,本地缓存段更新为1001-2000,更新的时候使用分布式锁来实现。(相当于用zookeeper实现了基于数据库的号段模式)

优点:

效率高。

缺点:

维护成本较高,不能同时满足多个系统对id的需求,不够灵活。

5、基于数据库的号段模式

号段模式的思想是客户端每次从数据库中取出一批id供程序使用,从表中获取本次id值的范围,如[1,1000],然后客户端将申请的号段[1,1000]加载到内存。表结构参考如下:

create table id_generator (
  id int(10) not null,
  max_id bigint(20) not null comment '当前最大id',
  step int(20) not null comment '号段的布长',
  biz_type    int(20) not null comment '业务类型',
  version int(20) not null comment '乐观锁版本号',
  primary key (`id`)
)

等这批号段id用完,再次向数据库申请新号段,对max_id字段做一次update操作(update id_generator set max_id = #{max_id+step}, version = version + 1 where version = # {version} and biz_type = xxx),update成功则说明新号段获取成功,新的号段范围是(max_id ,max_id +step]

进一步优化:

在号段消耗一半的时候,提前预留下一段号段。将预留号段时机提前,减少阻塞发生概率。一般称此为双buffer机制。不同业务可以设置不同的生成规则。

5.雪花算法

雪花算法(snowflake)是twitter公司内部分布式项目采用的id生成算法,开源后广受国内大厂的好评,在该算法影响下各大公司相继开发出各具特色的分布式生成器。

雪花算法,不依赖其它系统或数据库,以服务的方式部署,供其它服务调用,稳定性高,生成 id 的性能也非常高。

给每台机器分配一个唯一标识,然后通过下面的结构实现全局唯一id:

分布式id是什么意思(六种分布式ID生成方法)
  • 1位。未使用(二进制中最高位为1的都是负数,所以这个最高位固定是0)
  • 41位。毫秒级时间(41 位的长度可以使用 69 年)
  • 10位。包含5位datacenterid和5位workerid(10位的长度最多支持部署1024个节点)
  • 12位。最后12位是毫秒内的计数(12位的计数顺序号支持每个节点每毫秒产生4096个id序号)

由于在java中64bit的整数是long类型,所以在java中snowflake算法生成的id就是long来存储的。

优点:

  1. 生成性能高。
  2. 整体上按照时间自增排序。

缺点:

  1. 强依赖机器时钟,如果时钟回拨,可能会导致服务异常。
  2. 不能同时满足多个系统对id的需求,不够灵活。
  3. 在单机上是递增的,但是由于涉及到分布式环境,每台机器上的时钟不可能完全同步,会出现不是全局递增的情况。

6.tinyid

tinyid是滴滴开源的分布式id生成方案,开源地址见于参考文档1,只提供基于号段模式来生成id(加入了双buffer机制)。

7.uidgenerator

uidgenerator是由百度技术部开发,开源地址见于参考文档2,基于snowflake实现的优化算法。借用未来时间和双buffer来解决时间回拨与生成性能等问题,同时结合mysql进行id分配。

8.leaf

leaf是美团开源的分布式id生成方案,开源地址见于参考文档3。提供两种生成的id的方式:雪花算法模式和号段模式。可通过配置文件来指定。

leaf的雪花算法模式依赖于zookeeper,其workid的生成策略是基于zookeeper的顺序id来生成的;号段模式也是基于数据库的号段模式+双buffer机制实现的。