[java] 分布式id生成方案

在这里插入图片描述

1、UUID

实现方式

String uuid = UUID.randomUUID().toString().replaceAll("-","");

优点:

  • 生成简单,本地生成无网络消耗,具有唯一性

缺点:

  • 无序的字符串,不具备趋势自增特性
  • 没有具体的业务含义
  • 长度过长,存储以及查询对MySQL的性能消耗较大。

2、数据库自增id

实现方式

需要一个单独的MySQL表用来生成ID

CREATE DATABASE `SEQ_ID`;
CREATE TABLE SEQID.SEQUENCE_ID (
    id bigint(20) unsigned NOT NULL auto_increment, 
    stub char(10) NOT NULL default '',
    PRIMARY KEY (id),
    UNIQUE KEY value(stub)
) ENGINE=MyISAM;

begin:
replace into SEQUENCE_ID(stub) VALUES('anyword');
select last_insert_id();
commit; 

优点:

  • 实现简单,ID单调自增,数值类型查询速度快

缺点:

  • DB单点存在宕机风险,无法扛住高并发场景

那如果采用基于数据库的主从或者多主模式,就能够解决上面的高并发场景了吗,答案是否定的

问题

  • 主从复制间存在延时,主机刚刚插入一个新的id,还没有同步到从机就挂机了。从机变为主机后会生成重复id
  • 多主模式下要设置起始值和自增步长,且不利于后续扩容,而且实际上单个数据库自身压力还是大,依旧无法满足高并发场景。

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

号段模式是当下分布式ID生成器的主流实现方式之一,号段模式可以理解为从数据库批量的获取自增ID,每次从数据库取出一个号段范围,例如 (1,1000] 代表1000个ID,具体的业务服务将本号段,生成1~1000的自增ID并加载到内存。表结构如下:

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`)
) 
  • biz_type :代表不同业务类型
  • max_id :当前最大的可用id
  • step :代表号段的长度
  • version :是一个乐观锁,每次都更新version,保证并发时数据的正确性

等这批号段ID用完,再次向数据库申请新号段,对max_id字段做一次update操作,update max_id= max_id + step,update成功则说明新号段获取成功,新的号段范围是(max_id ,max_id +step]。

update id_generator set max_id = #{max_id+step}, version = version + 1 where version = # {version} and biz_type = XXX

由于多业务端可能同时操作,所以采用版本号version乐观锁方式更新,这种分布式ID生成方式不强依赖于数据库,不会频繁的访问数据库,对数据库的压力小很多

4、基于Redis模式

Redis也同样可以实现,原理就是利用redis的 incr命令实现ID的原子性自增。

优点:

  • 不依赖于数据库,灵活方便,且性能优于数据库。
  • 数字ID天然排序,对分页或者需要排序的结果很有帮助。

缺点:

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

  • 防止redis挂机需要配置集群,RDB方式会有延时,而AOF恢复的时间较长(要执行较多的incr命令)

5、雪花算法(Snowflake)模式

在这里插入图片描述
Snowflake生成的是Long类型的ID,一个Long类型占8个字节,每个字节占8比特,也就是说一个Long类型占64个比特。

  • 第一个bit位(1bit):代表正负,正数是0,负数是1,默认为0。
  • 时间戳部分(41bit):毫秒级的时间,(当前时间戳 - 固定开始时间戳)的差值,可以使产生的ID从更小的值开始;41位大概使用69年
  • 工作机器id(10bit):机房或者机器号组合都可以。
  • 序列号部分(12bit),自增值支持同一毫秒内同一个节点可以生成4096个ID

优点:

  • 不依赖于数据库,灵活方便,且性能优于数据库。
  • ID按照时间在单机上是递增的。

缺点:

  • 在单机上是递增的,但是由于涉及到分布式环境,每台机器上的时钟不可能完全同步,也许有时候也会出现不是全局递增的情况。

6、百度的uid-generator

uid-generator是基于Snowflake算法实现的,与原始的snowflake算法不同在于,uid-generator支持自定义时间戳、工作机器ID和 序列号 等各部分的位数,而且uid-generator中采用用户自定义workId的生成策略。

  • workId,占用了22个bit位。需要与数据库配合使用,需要新增一个WORKER_NODE表。当应用重启时会向数据库表中去插入一条数据,插入成功后返回的自增ID就是该机器的workId,应用运行时workId不变

  • 时间占用了28个bit位,时间的单位改成了秒

  • 序列化占用了13个bit位

解决时钟回拨问题

  • synchronized保证线程安全;
  • 如果时间有任何的回拨,那么直接抛出异常;
  • 如果当前时间和上一次是同一秒时间,那么sequence自增。如果同一秒内自增值超过2^13-1,那么就会自旋等待下一秒(getNextSecond);
  • 如果是新的一秒,那么sequence重新从0开始;

7、美团(Leaf)

美团的Leaf也是一个分布式ID生成框架。它非常全面,即支持号段模式,也支持snowflake模式。号段模式这里就不介绍了,和上面的分析类似。

Leaf中的snowflake模式和原始snowflake算法的不同点,也主要在workId的生成,Leaf中workId是基于ZooKeeper的顺序Id来生成的,每个应用在使用Leaf-snowflake时,在启动时都会都在Zookeeper中生成一个顺序Id,相当于一台机器对应一个顺序节点,也就是一个workId。

在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值