分布式ID生成方法的超详细分析(全)

前言

关于什么是分布式ID

数据量不是很多的时候,单一个数据库表可以支撑其业务,即使数据在大也可以主从复制
到一定量的数据时,实现分库分表的时候,就需要一个全局唯一的ID,订单的编号就是分布式ID

关于上面牵扯到的主从复制
可看我之前的文章进行查缺补漏
关于主从复制的超详细解析(全)

关于数据库的分布式ID可看我之前在Mycat种提及到

具体都有如下

在实现分库分表的情况下,数据库自增主键已无法保证自增主键的全局唯一。为此,Mycat 提供了全局 sequence

  • 本地文件
    优点:本地加载,读取速度较快
    缺点:抗风险能力差,Mycat 所在主机宕机后,无法读取本地文件

  • 数据库方式
    利用数据库一个表 来进行计数累加。但是并不是每次生成序列都读写数据库,这样效率太低。Mycat 会预加载一部分号段到 Mycat 的内存中,这样大部分读写序列都是在内存中完成的。如果内存中的号段用完了 Mycat 会再向数据库要一次

  • 时间戳方式
    全局序列ID= 64 位二进制 (42(毫秒)+5(机器 ID)+5(业务编码)+12(重复累加) 换算成十进制为 18 位数的
    long 类型,每毫秒可以并发 12 位二进制的累加。
    优点:配置简单
    缺点:18 位 ID 过长

而涉及到程序上实现的分布式ID可看下面的文章
可通过UUID、雪花算法等

1. UUID

具体UUID是什么以及可看我之前的文章
java之UUID.randomUUID().toString()详细解析(全)
这里直接push过来

UUID 是 通用唯一识别码(Universally Unique Identifier)的缩写,主要是让让分布式系统中的所有元素,都能有唯一的辨识信息。

  • 为了提高效率,常用的UUID可缩短至16位比特数值
  • 使用UUID的一个好处是可以为新的服务创建新的标识符

源码
生成的UUID标识符16位具体信息如下:

  • (1)当前日期和时间,值第一部分与时间有关(如果你在生成一个UUID之后,过几秒又生成一个UUID,则第一个部分不同,其余相同)
  • (2)时钟序列
  • (3)全局唯一的IEEE机器识别号(如果有网卡,从网卡MAC地址获得,没有网卡以其他方式获得)
 public static UUID randomUUID() {
       
        SecureRandom ng = Holder.numberGenerator;
        byte[] randomBytes = new byte[16];
        ng.nextBytes(randomBytes);
        randomBytes[6]  &= 0x0f;  /* clear version        */
        randomBytes[6]  |= 0x40;  /* set to version 4     */
        randomBytes[8]  &= 0x3f;  /* clear variant        */
        randomBytes[8]  |= 0x80;  /* set to IETF variant  */
        return new UUID(randomBytes);
}

通过randomUUID().toString()生成
随机生成UUID的标识符是UUID类中的方法
而UUID.randomUUID().toString()是javaJDK提供的一个自动生成主键的方法

public class ThreadDemo4 {
    public static void main(String[] args) {
        //创建ArrayList集合
      List<String> list = new ArrayList<>();

        for (int i = 0; i <30; i++) {

            new Thread(()->{
                //向集合添加内容
                list.add(UUID.randomUUID().toString());
                //从集合获取内容
                System.out.println(list);
            },String.valueOf(i)).start();
        }
    }
}

代码截图
在这里插入图片描述

2. 数据库自增

前言中也有提及到数据库的方式,但是mycat的方式是放一定的序列到内存中,如果宕机之后,前面的序列不会更改,但是刚开始的序列号和之前的序列号会断开,也可以保证高可用

这种数据库的自增是通过数据库种的auto_increment,

用它来实现分布式服务风险比较大

3. 数据库集群

改进上面单个数据库自增的模式,因为怕宕机
那就多加入一个从机,实现主从复制或者是双主模式等

具体的规则就是设置一个起始值和步长值,实现两个mysql的不一样值
具体设置的方法为:

set @@auto_increment_offset = 1;     -- 起始值
set @@auto_increment_increment = 2;  -- 步长

但是如果突然加进来第三个服务器,前面两个序列号就都要重改,也比较麻烦

4. 数据库号段

从数据库批量的获取自增ID,每次从数据库取出一个号段范围,生成的自增ID加载到内存中

这与一开始前言所提及到的mycat比较相像
但是mycat是已经把一部分号段放置到内存中
而这里是在数据库取一部分,再放置到内存中,逻辑不一样

加入号段之后,在每个号段还要加上乐观锁(在数据库表中增加一个version字段),防止高并发的数据量
关于这部分乐观锁可看我之前的文章补充一些知识点,这里就不再赘述
java中各类锁的机制详细解析(全)

不会频繁的访问数据库,对数据库的压力小很多,因为有了缓存在内存中了

5. redis模式

利用redis的 incr命令实现ID的原子性自增

考虑到redis持久化的问题。redis有两种持久化方式RDB和AOF:

(1)RDB会定时打一个快照进行持久化,假如连续自增但redis没及时持久化,而这会Redis挂掉了,重启Redis后会出现ID重复的情况。

(2)AOF会对每条写命令进行持久化,即使Redis挂掉了也不会出现ID重复的情况,但由于incr命令的特殊性,会导致Redis重启恢复的数据时间过长。

6. 雪花算法

指定机器 & 同一时刻 & 某一并发序列,是唯一的。据此可生成一个64 bits的唯一ID(long)

组成部分(64bit)
1.第一位 占用1bit,其值始终是0,没有实际作用。
2.时间戳 占用41bit,精确到毫秒,总共可以容纳约69年的时间。
3.工作机器id 占用10bit,其中高位5bit是数据中心ID,低位5bit是工作节点ID,做多可以容纳1024个节点。
4.序列号 占用12bit,每个节点每毫秒0开始不断累加,最多可以累加到4095,一共可以产生4096个ID。

SnowFlake算法在同一毫秒内最多可以生成多少个全局唯一ID呢: 同一毫秒的ID数量 = 1024 X 4096 = 4194304

最核心的是中间的10位工作机器ID的分配,做到自动生成workID,避免运维人员的去分配

关于雪花算法详细分析可看这篇文章
理解分布式id生成算法SnowFlake

7. 其他

关于其他的自增,分布式ID,各大公司都有其一套的的算法
比如:

  • 滴滴出品(TinyID)
    数据库中保存了可用的id号段,tinyid会将可用号段加载到内存中,之后生成id会直接内存中产生

  • 百度 (Uidgenerator)
    百度的雪花算法是通过数据库来生成workID

  • 美团(Leaf)
    美团是通过Zookeeper持久顺序节点的特性自动对snowflake节点配置wokerID

总结

方法优点缺点
UUID1.容易实现,产生快。2.ID唯一。3.无需要中心化服务器。4.不会泄露商业机密1.可读性过差。2.占用空间过多。3.影响数据库性能
数据库自增实现简单,ID单调自增,数值类型查询速度快。单点存在宕机风险,无法扛住高并发场景。
数据库集群解决DB单点问题不利于后续扩容,单点压力也会更大不适合高并发
数据库号段解决单点压力抗风险能力差,没有集群
redis中的icncr原子性自增保证不重复RDB挂掉后出现重复,AOF挂掉重启时间长
雪花算法所有生成的id按时间趋势递增。不依赖数据库等第三方系统,以服务的方式部署,稳定性更高,生成ID的性能也是非常高的。可以根据自身业务特性分配bit位,非常灵活。强依赖机器时钟,如果机器上时钟回拨,会导致发号重复或者服务会处于不可用状态。
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码农研究僧

你的鼓励将是我创作的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值