关于雪花算法:分布式系统ID的唯一性雪花算法

42次阅读

共计 3361 个字符,预计需要花费 9 分钟才能阅读完成。

1. 为什么须要分布式全局惟一 ID

2.ID 生成规定局部硬性要求

3.ID 生成规定的可用性要求

4. 现有的 ID 生成策略

5. 雪花算法 ID 生成策略

1. 为什么须要分布式全局惟一 ID
在简单的分布式高并发零碎中,往往在 一秒之内就会产生海量的数据 ,而且咱们要对这些数据进行 唯一性的标识 ,且还要 保障有序性 ,在咱们以往的开发应用中,UUID 以及自增 ID 这种生成策略,可能无奈满足一瞬间生成数据的唯一性和有序性,此时 一个可能生成全局惟一的 ID 生成规定是非常重要的

2.ID 生成规定局部硬性要求
2.1)全局惟一
不呈现反复的 ID 号,这是对 ID 最根本的要求。
2.2)趋势递增
在 mysql 的 InnoDB 引擎中应用的是聚簇索引,应用 BTree 构造来保留数据,用递增的 ID 能够让 BTree 不会产生微小的变动来保障写入性能。

当 B +Tree 插入的 主键值为自增 的时候:

当 B +Tree 插入的主键值为 随机值 uuid的时候:

2.1)枯燥递增
保障 下一个 ID 的值肯定要大于上一个 ID 的值 ,来 合乎排序等要求

2.3)信息安全
如果 ID 是自增 ID 或者某种规定的连续性 ID,歹意的扒取工作就比拟容易进行 (比方 退款 接口,轻易输出订单的自增 ID。或者 支付红包 的接口,轻易输出红包的自增 ID,以及察看一天 ID 的增量来判断零碎一天的订单量。)所以 在一些利用场景下,须要 ID 不规则,让这些歹意的扒取工作不好进行。

2.4)含工夫戳
这样就能 在开发中通过 id 来理解这条数据的生成工夫

3.ID 生成规定的可用性要求
3.1)高可用
发送一个获取分布式 ID 的申请,服务器就要 99.99999% 的状况下给咱们创立一个分布式惟一 ID。

3.2)低延时
发送一个获取分布式 ID 的申请,服务器要响应迅速。

3.3)高 qps
如果一秒钟有十万个申请同时发送给服务器,服务器也要同时创立十万个不同的分布式惟一递增有序 ID。

4. 现有的 ID 生成策略

4.1)Uuid
长处:
能够 保障唯一性

毛病:
4.1.1)无序 ,不能生成递增的有序数字。
4.1.2) 主键过长 ,不合乎主键越短越好的规定,在 where id = ” 时候会产生比拟开销。
4.1.3) 索引 B +Tree 的决裂(下面曾经介绍过)。

4.2)自增 id
长处 :保障 枯燥递增性,有序性。

毛病:
4.2.1)ID 很容易被人猜出来,不平安
4.2.2) 单机模式无奈承载高并发量 ,无奈一秒钟生成几十万个不同的 ID,不合乎高 qps 规定。
4.2.3)集群模式,假如第一台数据库 id 是奇数,第二台 id 是偶数,这种配置规定十分繁琐,而且如果要扩大到一百多台 mysql, 不易于扩大。

4.3)基于 redis 的全局 ID 策略
长处 :因为 redis 底层人造地保障了原子性,所以能够应用 incr 来操作,而且单机 redis 的 qps 就比拟高, 能够肯定水平保障高 qps。

毛病 :在集群模式下,redis 和 mysql 一样, 都须要设置不同的增长步长 ,这种配置形式极为繁琐, 而且不易于扩大。

5. 雪花算法 ID 生成策略
理解了下面这么多生成策略之后,咱们发现上述生成 ID 规定均不合乎生成规定,这个时候,雪花算法呈现了!

Twitter 的分布式雪花算法 snowflake,经测试 snowflake 每秒可能生成 26 万个自增可排序的 id

5.1)雪花算法的数据结构

咱们先来看一下雪花算法的数据结构图:

雪花算法由一个 64bit 的 long 类型形成,它将一个 long 类型 拆分成了四个号段,来别离示意不同的值 来保障全局惟一和有序性

5.2)雪花算法的号段解析

1bit 符号位:
不必,二进制中的最高位是符号位,1 示意正数,0 示意负数
生成的 id 个别都是负数,所以最高符号位为 0.

41bit 工夫戳位:
用来记录时间,毫秒数。
41 位能够用来示意 2^(41)- 1 个数字,如果只用来示意负数,那么这个值的范畴就是 0~2^(41)-1,减 1 是因为数字是从 0 开始算的,而不是 1.

2^(41)- 1 转化成单位年则是(2^(41)-1)/(1000606024365) = 69 年

10bit 工作过程位:
能够用来示意工作机器 id,能够部署在 2^(10)=1024 个节点,也能够用 5 位 workId(工作机器 ID)和 5 位 datacenterId(工作线程 Id),也能够多用几位来示意工作机器 ID,位数不固定。

12bit 序列号位:
12 位,可示意的最大正整数是 2^12 -1 = 4095,来示意同一机器一时间戳(毫秒)内产生的 4095 个 ID 序号。

5.3)雪花算法的优缺点

长处:
毫秒数在高位,整个 ID 都是趋势递增的。
不依赖数据库等第三方零碎,以服务的形式部署,稳定性高,合乎高 qps,高可用。
依据本身业务调配 bit 位,非常灵活

毛病:
依赖机器时钟 如果机器时钟重置,会导致反复 ID 生成。
在单机上递增,然而因为是分布式环境,每台机器上的时钟不同步,有时候会呈现不递增的状况。(不过大抵是递增的,而且齐全能保障趋势递增。)

5.4)雪花算法的代码实现

咱们能够应用糊涂工具生成:

<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-captcha</artifactId>
    <version>${hutool.version}</version>
</dependency>

ID 生成器:

package com.example.demo.meeting.util;

import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.IdUtil;
import org.apache.commons.lang3.SystemUtils;
import org.apache.commons.lang3.RandomUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

import java.net.Inet4Address;
import java.net.UnknownHostException;

/**
 * @author sulingfeng
 * @title: IdGenerator
 */
@Component
public class IdGenerator {public static Snowflake snowflake = IdUtil.createSnowflake(getWorkId(), getDataCenterId());
    /**
     * workId 应用 IP 生成
     * @return workId
     */
    private static Long getWorkId() {
        try {String hostAddress = Inet4Address.getLocalHost().getHostAddress();
            int[] ints = StringUtils.toCodePoints(hostAddress);
            int sums = 0;
            for (int b : ints) {sums = sums + b;}
            // 咱们能够依据须要自行管制长短
            return (long) (sums % 32);
        }
        catch (UnknownHostException e) {
            // 失败就随机
            return RandomUtils.nextLong(0, 31);
        }
    }


    /**
     * dataCenterId 应用 hostName 生成
     * @return dataCenterId
     */
    private static Long getDataCenterId() {
        try {String hostName = SystemUtils.getHostName();
            int[] ints = StringUtils.toCodePoints(hostName);
            int sums = 0;
            for (int i: ints) {sums = sums + i;}
            return (long) (sums % 32);
        }
        catch (Exception e) {
            // 失败就随机
            return RandomUtils.nextLong(0, 31);
        }
    }

    public synchronized long snowflakeId() {return snowflake.nextId();
    }

}

正文完
 0