48 分布式id的生成策略

这篇具有很好参考价值的文章主要介绍了48 分布式id的生成策略。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1.UUID
1.UUID (Universally Unique Identifier),通用唯一识别码。UUID是基于当前时间、计数器(counter)和硬件标识(通常为无线网卡的MAC地址)等数据计算生成的。

UUID由以下几部分的组合:

1.当前日期和时间,UUID的第一个部分与时间有关,如果你在生成一个UUID之后,过几秒又生成一个UUID,则第一个部分不同,其余相同。
2.时钟序列。
3.全局唯一的IEEE机器识别号,如果有网卡,从网卡MAC地址获得,没有网卡以其他方式获得。

UUID 是由一组32位数的16进制数字所构成,以连字号分隔的五组来显示,形式为 8-4-4-4-12,总共有 36个字符(即三十二个英数字母和四个连字号)。例如:

aefbbd3a-9cc5-4655-8363-a2a43e6e6c80 
xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx

48 分布式id的生成策略,java进阶,分布式
如果需求是只保证唯一性,那么UUID也是可以使用的,但是按照上面的分布式id的要求, UUID其实是不能做成分布式id的,原因如下:

1.首先分布式id一般都会作为主键,但是安装mysql官方推荐主键要尽量越短越好,UUID每一个都很长,所以不是很推荐。
2.既然分布式id是主键,然后主键是包含索引的,然后mysql的索引是通过b+树来实现的,每一次新的UUID数据的插入,为了查询的优化,都会对索引底层的b+树进行修改,因为UUID数据是无序的,所以每一次UUID数据的插入都会对主键生成的b+树进行很大的修改,这一点很不好。
3.信息不安全:基于MAC地址生成UUID的算法可能会造成MAC地址泄露,这个漏洞曾被用于寻找梅丽莎病毒的制作者位置。
2.自增ID

这种方式在单个数据库的场景中是可以这样做的,但如果是在分库分表的环境下。直接利用单个数据库的自增肯定会出现问题。因为ID要唯一,但是分表分库后只能保证一个表中的ID的唯一,而不能保证整体的ID唯一。
48 分布式id的生成策略,java进阶,分布式
上面的情况我们可以通过单独创建主键维护表来处理。
48 分布式id的生成策略,java进阶,分布式

3.数据库多主模式

单点数据库方式存在明显的性能问题,可以对数据库进行优化,担心一个主节点挂掉没法使用,可以选择做双主模式集群,也就是两个MySQL实例都能单独生产自增的ID。

可以设置主键自增的步长从2开始:
48 分布式id的生成策略,java进阶,分布式
这种在并发量比较高的情况下,如何保证扩展性其实会是一个问题。在高并发情况下无能为力。

4.号段模式

例如 (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用完,再次向数据库申请新号段,对maxid字段做一次update操作,update maxid= max_id + step,update成功则说明新号段获取成功,新的号段范围是(maxid ,maxid +step]
48 分布式id的生成策略,java进阶,分布式
由于多业务端可能同时操作,所以采用版本号version乐观锁方式更新,这种分布式ID生成方式不强依赖于数据库,不会频繁的访问数据库,对数据库的压力小很多。但同样也会存在一些缺点比如:服务器重启,单点故障会造成ID不连续。

5.Redis

基于全局唯一ID的特性,通过Redis的INCR命令来生成全局唯一ID。
48 分布式id的生成策略,java进阶,分布式
同样使用Redis也有对应的缺点:

1.ID 生成的持久化问题,如果Redis宕机了怎么进行恢复
2.节点宕机问题

针对故障问题我们可以通过Redis集群来处理,比如我们有三个Redis的Master节点。可以初始化每台Redis的值分别是1,2,3,然后分别把分布式ID的KEY用Hash Tags固定每一个master节点,步长就是master节点的个数。各个Redis生成的ID为:

节点A:1,4,7
节点B:2,5,8
节点C:3,6,9

优点:

1.不依赖于数据库,灵活方便,且性能优于数据库
2.数字ID有序,对分页处理和排序都很友好
3.防止了Redis的单机故障

缺点:

1.如果没有Redis数据库,需要安装配置,增加复杂度
2.集群节点确定是3个后,后面调整不是很友好
public class RedisDistributedId {

    @Autowired
    private StringRedisTemplate redisTemplate;

    private static final long BEGIN_TIMESTAMP = 1659312000l;

    /**
     * 生成分布式ID
     * 符号位    时间戳[31位]  自增序号【32位】
     * @param item
     * @return
     */
    public long nextId(String item){
        // 1.生成时间戳
        LocalDateTime now = LocalDateTime.now();
        // 格林威治时间差
        long nowSecond = now.toEpochSecond(ZoneOffset.UTC);
        // 我们需要获取的 时间戳 信息
        long timestamp = nowSecond - BEGIN_TIMESTAMP;
        // 2.生成序号 --》 从Redis中获取
        // 当前当前的日期
        String date = now.format(DateTimeFormatter.ofPattern("yyyy:MM:dd"));
        // 获取对应的自增的序号
        Long increment = redisTemplate.opsForValue().increment("id:" + item + ":" + date);
        return timestamp << 32 | increment;
    }
}

6.雪花算法

Snowflake,雪花算法是有Twitter开源的分布式ID生成算法,以划分命名空间的方式将64bit位分割成了多个部分,每个部分都有具体的不同含义,在Java中64Bit位的整数是Long类型,所以在Java中Snowflake算法生成的ID就是long来存储的。具体如下:
48 分布式id的生成策略,java进阶,分布式
第一部分:占用1bit,第一位为符号位,不适用
第二部分:41位的时间戳,41bit位可以表示241个数,每个数代表的是毫秒,那么雪花算法的时间年限是(241)/(1000×60×60×24×365)=69年
第三部分:10bit表示是机器数,即 2^ 10 = 1024台机器,通常不会部署这么多机器
第四部分:12bit位是自增序列,可以表示2^12=4096个数,一秒内可以生成4096个ID

public class SnowflakeIdWorker {

    /**
     * 开始时间截 (2020-11-03,一旦确定不可更改,否则时间被回调,或者改变,可能会造成id重复或冲突)
     */
    private final long twepoch = 1604374294980L;

    /**
     * 机器id所占的位数
     */
    private final long workerIdBits = 5L;

    /**
     * 数据标识id所占的位数
     */
    private final long datacenterIdBits = 5L;

    /**
     * 支持的最大机器id,结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数)
     */
    private final long maxWorkerId = -1L ^ (-1L << workerIdBits);

    /**
     * 支持的最大数据标识id,结果是31
     */
    private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);

    /**
     * 序列在id中占的位数
     */
    private final long sequenceBits = 12L;

    /**
     * 机器ID向左移12位
     */
    private final long workerIdShift = sequenceBits;

    /**
     * 数据标识id向左移17位(12+5)
     */
    private final long datacenterIdShift = sequenceBits + workerIdBits;

    /**
     * 时间截向左移22位(5+5+12)
     */
    private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;

    /**
     * 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095)
     */
    private final long sequenceMask = -1L ^ (-1L << sequenceBits);

    /**
     * 工作机器ID(0~31)
     */
    private long workerId;

    /**
     * 数据中心ID(0~31)
     */
    private long datacenterId;

    /**
     * 毫秒内序列(0~4095)
     */
    private long sequence = 0L;

    /**
     * 上次生成ID的时间截
     */
    private long lastTimestamp = -1L;

//==============================Constructors=====================================

    /**
     * 构造函数
     *
     */
    public SnowflakeIdWorker() {
        this.workerId = 0L;
        this.datacenterId = 0L;
    }

    /**
     * 构造函数
     *
     * @param workerId     工作ID (0~31)
     * @param datacenterId 数据中心ID (0~31)
     */
    public SnowflakeIdWorker(long workerId, long datacenterId) {
        if (workerId > maxWorkerId || workerId < 0) {
            throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
        }
        if (datacenterId > maxDatacenterId || datacenterId < 0) {
            throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
        }
        this.workerId = workerId;
        this.datacenterId = datacenterId;
    }

// ==============================Methods==========================================

    /**
     * 获得下一个ID (该方法是线程安全的)
     *
     * @return SnowflakeId
     */
    public synchronized long nextId() {
        long timestamp = timeGen();

        //如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常
        if (timestamp < lastTimestamp) {
            throw new RuntimeException(
                    String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
        }

        //如果是同一时间生成的,则进行毫秒内序列
        if (lastTimestamp == timestamp) {
            sequence = (sequence + 1) & sequenceMask;
            //毫秒内序列溢出
            if (sequence == 0) {
                //阻塞到下一个毫秒,获得新的时间戳
                timestamp = tilNextMillis(lastTimestamp);
            }
        }
        //时间戳改变,毫秒内序列重置
        else {
            sequence = 0L;
        }

        //上次生成ID的时间截
        lastTimestamp = timestamp;

        //移位并通过或运算拼到一起组成64位的ID
        return ((timestamp - twepoch) << timestampLeftShift) //
                | (datacenterId << datacenterIdShift) //
                | (workerId << workerIdShift) //
                | sequence;
    }

    /**
     * 阻塞到下一个毫秒,直到获得新的时间戳
     *
     * @param lastTimestamp 上次生成ID的时间截
     * @return 当前时间戳
     */
    protected long tilNextMillis(long lastTimestamp) {
        long timestamp = timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = timeGen();
        }
        return timestamp;
    }

    /**
     * 返回以毫秒为单位的当前时间
     *
     * @return 当前时间(毫秒)
     */
    protected long timeGen() {
        return System.currentTimeMillis();
    }

    /**
     * 随机id生成,使用雪花算法
     *
     * @return
     */
    public static String getSnowId() {
        SnowflakeIdWorker sf = new SnowflakeIdWorker();
        String id = String.valueOf(sf.nextId());
        return id;
    }

//=========================================Test=========================================

    /**
     * 测试
     */
    public static void main(String[] args) {
        SnowflakeIdWorker idWorker = new SnowflakeIdWorker(0, 0);
        for (int i = 0; i < 1000; i++) {
            long id = idWorker.nextId();
            System.out.println(id);
        }
    }
}

实际生产环境中应该怎么来应用雪花算法来实现分布式ID。
48 分布式id的生成策略,java进阶,分布式

7.百度(Uidgenerator)
1.源码地址:https://github.com/baidu/uid-generator
2.中文文档地址:https://github.com/baidu/uid-generator/blob/master/README.zh_cn.md

UidGenerator是百度开源的Java语言实现,基于Snowflake算法的唯一ID生成器。它是分布式的,并克服了雪花算法的并发限制。单个实例的QPS能超过6000000。需要的环境:JDK8+,MySQL(用于分配WorkerId)。

百度的Uidgenerator对结构做了部分的调整,具体如下:
48 分布式id的生成策略,java进阶,分布式

8.美团(Leaf)

由美团开发,开源项目链接:https://github.com/Meituan-Dianping/Leaf

Leaf同时支持号段模式和snowflake算法模式,可以切换使用。ID号码是趋势递增的8byte的64位数字,满足上述数据库存储的主键要求。

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

Leaf的号段模式是对直接用数据库自增ID充当分布式ID的一种优化,减少对数据库的频率操作。相当于从数据库批量的获取自增ID,每次从数据库取出一个号段范围,例如 (1,1000] 代表1000个ID,业务服务将号段在本地生成1~1000的自增ID并加载到内存.。

特性:

1.全局唯一,绝对不会出现重复的ID,且ID整体趋势递增。
2.高可用,服务完全基于分布式架构,即使MySQL宕机,也能容忍一段时间的数据库不可用。
3.高并发低延时,在CentOS 4C8G的虚拟机上,远程调用QPS可达5W+TP991ms内。
4.接入简单,直接通过公司RPC服务或者HTTP调用即可接入。

Leaf采用双buffer的方式,它的服务内部有两个号段缓存区segment。当前号段已消耗10%时,还没能拿到下一个号段,则会另启一个更新线程去更新下一个号段。

简而言之就是Leaf保证了总是会多缓存两个号段,即便哪一时刻数据库挂了,也会保证发号服务可以正常工作一段时间。

<dependency>
            <groupId>com.sankuai.inf.leaf</groupId>
            <artifactId>leaf-boot-starter</artifactId>
            <version>1.0.1-RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-recipes</artifactId>
            <version>2.6.0</version>
            <exclusions>
                <exclusion>
                    <groupId>log4j</groupId>
                    <artifactId>log4j</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

属性文件配置:

 leaf.name=com.boge.vip
 leaf.segment.enable=false
 leaf.segment.url=
 leaf.segment.username=
 leaf.segment.password=
 leaf.snowflake.enable=true
 leaf.snowflake.address=
 leaf.snowflake.port=
 leaf.snowflake.address=192.168.56.100
 leaf.snowflake.port=2181

放开注解:
48 分布式id的生成策略,java进阶,分布式
注意你还需要启动一个Zookeeper服务。
测试代码:

 @SpringBootTest
 class DistributedIdsVipApplicationTests {
	@Autowired
 RedisDistributedId redisDistributedId;

 @Autowired
 private SnowflakeService snowflakeService;

@Test
void contextLoads() {
    Result abc = snowflakeService.getId("abc");
    System.out.println(abc.getId());

    System.out.println(Long.toBinaryString(abc.getId()));
}
 }

48 分布式id的生成策略,java进阶,分布式

9.滴滴(TinyID)

由滴滴开发,开源项目链接:https://github.com/didi/tinyid

Tinyid是在美团(Leaf)的leaf-segment算法基础上升级而来,不仅支持了数据库多主节点模式,还提供了tinyid-client客户端的接入方式,使用起来更加方便。但和美团(Leaf)不同的是,Tinyid只支持号段一种模式不支持雪花模式。Tinyid提供了两种调用方式,一种基于Tinyid-server提供的http方式,另一种Tinyid-client客户端方式。每个服务获取一个号段(1000,2000]、(2000,3000]、(3000,4000]

特性:

1.全局唯一的longID
2.趋势递增的id
3.提供 http 和 java-client 方式接入
4.支持批量获取ID
5.支持生成1,3,5,7,9...序列的ID
6.支持多个db的配置

适用场景:只关心ID是数字,趋势递增的系统,可以容忍ID不连续,可以容忍ID的浪费
不适用场景:像类似于订单ID的业务,因生成的ID大部分是连续的,容易被扫库、或者推算出订单量等信息文章来源地址https://www.toymoban.com/news/detail-797143.html

到了这里,关于48 分布式id的生成策略的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 分布式—雪花算法生成ID

    由64个Bit(比特)位组成的long类型的数字 0 | 0000000000 0000000000 0000000000 000000000 | 00000 | 00000 | 000000000000 1个bit:符号位,始终为0。 41个bit:时间戳,精确到毫秒级别,可以使用69年。 10个bit:工作机器ID,可以部署在1024个节点上。 12个bit:序列号,每个节点每毫秒内最多可以生成

    2024年02月11日
    浏览(45)
  • 76、分布式id生成方案

    1,当前日期和时间 时间戳 2,时钟序列。 计数器 3,全局唯一的IEEE机器识别号,如果有网卡,从网卡MAC地址获得,没有网卡以其他方式获得。 优点: 代码简单,性能好(本地生成,没有网络消耗),保证唯一(相对而言,重复概率极低可以忽略) 缺点: 每次生成的ID都是无序的,

    2024年02月16日
    浏览(50)
  • 分布式ID生成算法——雪花算法

    一、分布式ID ID可以唯一标识一条记录。 对于单体架构,我们可以使用自增ID来保证ID的唯一性。但是,在分布式系统中,简单的使用自增ID就会导致ID冲突。这也就引出了 分布式ID 问题。分布式ID也要求满足分布式系统的 高性能、高可用、高并发 的特点。 二、雪花算法 世界

    2024年02月06日
    浏览(51)
  • 分布式ID生成算法:雪花算法

    雪花算法(Snowflake)是一种分布式ID生成算法,可以生成唯一的、有序的、不重复的ID号,广泛应用于分布式系统中。其生成的ID号由64位二进制数组成,可以转换成16进制或10进制的字符串表示。 雪花算法的核心思想是将一个64位的二进制数分成四部分,分别表示时间戳、数据

    2024年02月15日
    浏览(41)
  • 无分布式锁的ID生成

    TEAM GARDEN 本来ID是自增的,后面发现自增ID比较麻烦,有问题: 不可控的间隔: 如果你在插入数据时,中途删除了一些行,导致自增的ID出现间隔,那么新插入的行会填充这些间隔,可能会导致ID序列不连续,不利于数据分析和理解。 不适用于批量插入: 在批量插入数据时,

    2024年02月11日
    浏览(42)
  • 雪花算法生成分布式主键ID

    直接上代码,复制即可使用 在这个示例中,你可以通过 SnowflakeIdGenerator.init(dataCenterId, workerId); 初始化数据中心 ID 和工作 ID,然后通过 SnowflakeIdGenerator.generateId(); 静态方法生成 Snowflake ID 的字符串形式。

    2024年02月22日
    浏览(48)
  • 【智能排班系统】雪花算法生成分布式ID

    在复杂而庞大的分布式系统中,确保数据实体的唯一标识性是一项至关重要的任务,生成全局唯一且有序的ID生成机制成为必不可少的环节。雪花算法(Snowflake Algorithm)正是为此目的而生,以其简洁的设计、高效的表现与良好的扩展性赢得了业界的广泛认可。 雪花算法最早由

    2024年04月10日
    浏览(85)
  • 分布式Id生成之雪花算法(SnowFlake)

    目录 前言 回顾二进制 二进制概念 运算法则 位(Bit) 字节(Byte) 字符 字符集 二进制原码、反码、补码 有符号数和无符号数 疑问:为什么不是-127 ~ 127 ? 为什么需要分布式全局唯一ID以及分布式ID得业务需求? ID生成规则部分硬性要求 ID生成系统的可用性要求 通用解决方

    2024年02月11日
    浏览(35)
  • 分布式唯一ID生成算法——雪花算法(SnowFlake)

    SnowFlake算法 据国家大气研究中心的查尔斯·奈特称,一般的雪花大约由10^19个水分子组成。在雪花形成过程中,会形成不同的结构分支,所以说大自然中不存在两片完全一样的雪花,每一片雪花都拥有自己漂亮独特的形状。 雪花算法表示生成的id如雪花般独一无二。 snowflake是

    2023年04月20日
    浏览(47)
  • 分布式id生成方案及springboot进行集成

    UUID(Universally Unique Identifier) 即通用唯一识别码,是一种由网络软件使用的标识符,它是由IP地址、当前时间戳、随机数、节点等多个部分组成,具有唯一性。但是,UUID方案的缺点是,生成的id较长,不便于存储和使用。 Snowflake算法 它是Twitter公司开源的一个分布式唯一ID生成器

    2023年04月08日
    浏览(40)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包