加载中 ...

分布式唯一ID的几种生成方案

2019-07-10 17:00:15 来源:沈阳小程序开发 作者:沈阳软件开发

前言

在因特网的商业系统中,涉及各种ID,例如支付系统中的支付ID,退款ID等。一般来说,生成ID的解决方案是什么?特别是在复杂的分布式系统业务场景中,我们选择适合您的解决方案非常重要。我们一个一个地看看它们。并非所有这些都适合。这些解决方案仅供您参考,可能对您有用。

正文

分布式ID功能

唯一性:确实是沈阳app开发

ad.jpg

保证生成的ID对整个网络而言是唯一的。

有序升序:确保为特定用户或企业生成的ID增加一定数量。

高可用性:确保始终正确生成ID。

花时间:ID包含时间,当您扫描它时,您知道交易的哪一天。

分布式ID生成方案

1. UUID

该算法的核心思想是结合机器的网卡,本地时间和计数来生成UUID。

优点:本地生成,简单构建,良好性能,无高可用性风险

缺点:长度长,存储冗余,无序和不可读,查询效率低

2.数据库自动增量ID

使用数据库的id自动增量策略,例如MySQL的auto_increment。并且您可以使用两个数据库来设置非同步长度并生成没有重复ID的策略以实现高可用性。

优点:数据库生成的ID绝对有序,高可用性实现很简单

缺点:需要独立部署数据库实例,成本高,性能瓶颈

3.批量生成ID

按需批量生成多个ID,每次沈阳需要访问数据库,数据库被修改为最大ID值,当前值和最大值记录在内存中。

优点:避免每次生成ID时都必须访问数据库并带来压力并提高性能

缺点:它属于本地生成策略,存在单点故障,服务重启导致ID不连续

4. Redis生成ID

Redis的所有命令操作都是单线程的,提供自增量的原子命令,如incr和increby,因此您可以保证生成的ID绝对是唯一的。

优点:它不依赖于数据库,灵活方便,性能优于数据库;数字ID是自然排序的,这有助于分页或排序结果。

缺点:如果系统中没有Redis,则需要引入新组件以增加系统复杂性;编码和配置的工作量相对较大。

考虑到单个节点的性能瓶颈,可以使用Redis群集来实现更高的吞吐量。假设集群中有5个Redis。您可以将每个Redis值初始化为1,2,3,4,5,然后步长为5.每个Redis生成的ID为:

答:1,6,11,16,21

B:2,7,12,17,22

C:3,8,13,18,23

D:4,9,14,19,24

E:5,10,15,20,25

将来很难做出改变。步长和初始值必须提前确定。使用Redis群集也可能是单点故障。

此外,使用Redis生成每天从0开始的序列号更合适。例如,订单号=日期+天自增长数。您可以使用INCR进行累积,每天生成一个Redis密钥。

5. Twitter的雪花算法

Twitter使用zookeeper实现全局ID生成服务Snowflake

Twitter的Snowflake算法由以下部分组成:

1位符号:

由于long类型是用java签名的,最高位是符号位,正数是0,负数是1,实际系统中使用的ID一般是正数,所以最高位是0。/p>

41位时间戳(毫秒):

需要注意的是,这里的41位时间戳不是当前时间的时间戳,而是时间戳(当前时间戳 - 开始时间戳)的差值,其中起始时间戳通常由ID生成器启动。使用的时间戳由程序指定,因此41位毫秒时间戳最多可用于(1

10位数据机位:

包括5个数据识别位和5个机器识别位,这10个位确定分布式系统中1<<<>

的最大部署

在12位毫秒内完成序列:

该12位计数每毫秒每个节点最多支持1

(同一台机器,同一时间)

加起来只有64位,长类型。

优点:高性能,低延迟,按时间排序,一般不会导致ID冲突

缺点:需要独立开发和部署,依靠机器的时钟

简单的实施

公共类IdWorker {

/**

*开始时间戳2017-04-01

*/

私人最终长纪元=1491004800000L;

/**

*机器ID中的位数

*/

私人最终长工人IdBits=5L;

/**

* ID ID中的位数

*/

私有最终长数据中心ID=5L;

/**

*支持的最大机器ID,结果为31

*/

私有最终长maxWorkerId=〜(-1L<<>

/**

*支持的最大数据ID,结果为31

*/

私有最终长maxDataCenterId=〜(-1<<>

/**

* id中的位数(以毫秒为单位)

*/

私有最终长sequenceBits=12L;

/**

*机器ID向左移动12位

*/

私有最终long workerIdShift=sequenceBits;

/**

*数据标识ID向左移动17(12 + 5)个数字

*/

私有最终长数据CenterIdShift=sequenceBits + workerIdBits;

/**

*时间戳向左移动了22(12 + 5 + 5)位

*/

私有最终long timestampShift=sequenceBits + workerIdBits + dataCenterIdBits;

/**

*生成序列的掩码,这里是4095(0b111111111111=0xfff=4095)

*/

私有final long sequenceMask=〜(-1L<<<>

/**

*数据ID(0~31)

*/

Private long dataCenterId;

/**

*机器ID(0~31)

*/

私人长工人;

/**

*序列在毫秒(0~4095)

*/

私人长序列;

/**

*上次生成的ID的时间戳

*/

私有long lastTimestamp=-1L;

Public IdWorker(long dataCenterId,long workerId){

如果(dataCenterId> maxDataCenterId || dataCenterId< 0){

抛出新的IllegalArgumentException(String.format('dataCenterId不能大于%d或小于0',maxDataCenterId));

}

如果(workerId> maxWorkerId || workerId< 0){

抛出新的IllegalArgumentException(String.format('worker Id不能大于%d或小于0',maxWorkerId));

}

this.dataCenterId=dataCenterId;

this.workerId=workerId;

}

/**

*获取下一个ID(此方法是线程安全的)

* @return snowflakeId

*/

公共同步long nextId(){

长时间戳=timeGen();

//如果当前时间小于前一个ID生成的时间戳,则系统时钟已经回滚,这次应该抛出异常

如果(timestamp< lastTimestamp){

抛出新的RuntimeException(String.format('Clock向后移动。拒绝为%d毫秒生成id',lastTimestamp - timestamp));

}

//如果它是同时生成的,则序列在毫秒内

如果(timestamp==lastTimestamp){

序列=(序列+ 1)& sequenceMask;

//序列溢出(毫秒)

如果(序列==0){

//阻止到下一个毫秒并获得一个新的时间戳

时间戳=nextMillis(lastTimestamp);

}

} else {//时间戳已更改,序列重置以毫秒为单位

序列=0L;

}

lastTimestamp=timestamp;

//通过位或操作移位并放在一起形成64位ID

返回((timestamp - epoch)<<>

(dataCenterId<<>

(workerId<<>

序列;

}

/**

*以毫秒为单位返回当前时间

* @return当前时间(毫秒)

*/

受保护的long timeGen(){

返回System.currentTimeMillis();

}

/**

*阻止直到下一毫秒,直到获得新的时间戳

* @param lastTimestamp上次生成ID的时间

* @return当前时间戳

*/

受保护的long nextMillis(long lastTimestamp){

长时间戳=timeGen();

而(timestamp<=>

时间戳=lastTimestamp;

}

返回时间戳;

}

}

6.百度UidGenerator

UidGenerator是百度的开源分布式ID生成器。基于雪花算法的实现,似乎没问题。但是,国内开源项目的维护确实令人担忧。

7.美国使命叶

Leaf是Meituan的开源分布式ID生成器,它保证了全局唯一性,趋势增量,单调增量和信息安全性。它还提到了几种分布式方案的比较,但也需要关系数据库,ZooKeeper和其他中间件。

总结

本文与您分享了全球id生成服务的几种常见场景,并比较了它们的优缺点和适用场景。在实际工作中,您可以结合业务和系统架构系统进行合理选择。

“沈阳软件公司”的新闻页面文章、图片、音频、视频等稿件均为自媒体人、第三方机构发布或转载。如稿件涉及版权等问题,请与

我们联系删除或处理,客服QQ:55506560,稿件内容仅为传递更多信息之目的,不代表本网观点,亦不代表本网站赞同

其观点或证实其内容的真实性。