分布式唯一ID的几种生成方案
前言
在因特网的商业系统中,涉及各种ID,例如支付系统中的支付ID,退款ID等。一般来说,生成ID的解决方案是什么?特别是在复杂的分布式系统业务场景中,我们选择适合您的解决方案非常重要。我们一个一个地看看它们。并非所有这些都适合。这些解决方案仅供您参考,可能对您有用。
正文
分布式ID功能
保证生成的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,稿件内容仅为传递更多信息之目的,不代表本网观点,亦不代表本网站赞同
其观点或证实其内容的真实性。
热门文章
使用“扫一扫”即可将网页分享至朋友圈。
上一篇:
战斗很多:奇迹背后的矛盾
下一篇:很抱歉没有了